diff options
98 files changed, 3905 insertions, 1137 deletions
diff --git a/Android.mk b/Android.mk index c1c74ea04e55..b95ab2e9d0dd 100644 --- a/Android.mk +++ b/Android.mk @@ -345,6 +345,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IMediaRouterService.aidl \ media/java/android/media/IMediaScannerListener.aidl \ media/java/android/media/IMediaScannerService.aidl \ + media/java/android/media/IRecordingConfigDispatcher.aidl \ media/java/android/media/IRemoteDisplayCallback.aidl \ media/java/android/media/IRemoteDisplayProvider.aidl \ media/java/android/media/IRemoteVolumeController.aidl \ diff --git a/api/current.txt b/api/current.txt index b0eb006e5b5a..629d832c0c3c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5793,6 +5793,7 @@ package android.app.admin { method public boolean getCameraDisabled(android.content.ComponentName); method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException; method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName); + method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); method public java.lang.String getDeviceOwnerLockScreenInfo(); @@ -5857,6 +5858,7 @@ package android.app.admin { method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); + method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); @@ -12784,6 +12786,7 @@ package android.graphics.drawable { method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable); field public static final int PADDING_MODE_NEST = 0; // 0x0 field public static final int PADDING_MODE_STACK = 1; // 0x1 + field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000 } public class LevelListDrawable extends android.graphics.drawable.DrawableContainer { @@ -20368,8 +20371,6 @@ package android.media { field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc"; field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp"; field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc"; field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; @@ -22579,7 +22580,16 @@ package android.mtp { public class MtpEvent { ctor public MtpEvent(); + method public int getDevicePropCode(); method public int getEventCode(); + method public int getObjectFormatCode(); + method public int getObjectHandle(); + method public int getObjectPropCode(); + method public int getParameter1(); + method public int getParameter2(); + method public int getParameter3(); + method public int getStorageId(); + method public int getTransactionId(); } public final class MtpObjectInfo { @@ -49542,6 +49552,8 @@ package java.lang { public class InternalError extends java.lang.VirtualMachineError { ctor public InternalError(); ctor public InternalError(java.lang.String); + ctor public InternalError(java.lang.String, java.lang.Throwable); + ctor public InternalError(java.lang.Throwable); } public class InterruptedException extends java.lang.Exception { @@ -50314,6 +50326,8 @@ package java.lang { public abstract class VirtualMachineError extends java.lang.Error { ctor public VirtualMachineError(); ctor public VirtualMachineError(java.lang.String); + ctor public VirtualMachineError(java.lang.String, java.lang.Throwable); + ctor public VirtualMachineError(java.lang.Throwable); } public final class Void { @@ -54056,6 +54070,7 @@ package java.security.cert { method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); } public class CertPathBuilderException extends java.security.GeneralSecurityException { @@ -54073,6 +54088,13 @@ package java.security.cert { public abstract class CertPathBuilderSpi { ctor public CertPathBuilderSpi(); method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException; + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); + } + + public abstract interface CertPathChecker { + method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; + method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; + method public abstract boolean isForwardCheckingSupported(); } public abstract interface CertPathParameters implements java.lang.Cloneable { @@ -54087,6 +54109,7 @@ package java.security.cert { method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -54123,6 +54146,7 @@ package java.security.cert { public abstract class CertPathValidatorSpi { ctor public CertPathValidatorSpi(); + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -54281,9 +54305,10 @@ package java.security.cert { method public java.security.cert.CertPath getCertPath(); } - public abstract class PKIXCertPathChecker implements java.lang.Cloneable { + public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable { ctor protected PKIXCertPathChecker(); method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException; + method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; method public java.lang.Object clone(); method public abstract java.util.Set<java.lang.String> getSupportedExtensions(); method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; @@ -54343,6 +54368,30 @@ package java.security.cert { enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT; } + public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker { + ctor protected PKIXRevocationChecker(); + method public java.util.List<java.security.cert.Extension> getOcspExtensions(); + method public java.net.URI getOcspResponder(); + method public java.security.cert.X509Certificate getOcspResponderCert(); + method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses(); + method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions(); + method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions(); + method public void setOcspExtensions(java.util.List<java.security.cert.Extension>); + method public void setOcspResponder(java.net.URI); + method public void setOcspResponderCert(java.security.cert.X509Certificate); + method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>); + method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>); + } + + public static final class PKIXRevocationChecker.Option extends java.lang.Enum { + method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String); + method public static final java.security.cert.PKIXRevocationChecker.Option[] values(); + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL; + } + public abstract interface PolicyNode { method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren(); method public abstract int getDepth(); @@ -54502,6 +54551,7 @@ package java.security.cert { method public javax.security.auth.x500.X500Principal getSubjectX500Principal(); method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException; method public abstract int getVersion(); + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract interface X509Extension { diff --git a/api/system-current.txt b/api/system-current.txt index bd7dc9742a5a..6a36ce05acca 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5919,6 +5919,7 @@ package android.app.admin { method public boolean getCameraDisabled(android.content.ComponentName); method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException; method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName); + method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); method public deprecated java.lang.String getDeviceInitializerApp(); @@ -5992,6 +5993,7 @@ package android.app.admin { method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); + method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); @@ -13139,6 +13141,7 @@ package android.graphics.drawable { method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable); field public static final int PADDING_MODE_NEST = 0; // 0x0 field public static final int PADDING_MODE_STACK = 1; // 0x1 + field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000 } public class LevelListDrawable extends android.graphics.drawable.DrawableContainer { @@ -21667,8 +21670,6 @@ package android.media { field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc"; field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp"; field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc"; field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; @@ -24126,7 +24127,16 @@ package android.mtp { public class MtpEvent { ctor public MtpEvent(); + method public int getDevicePropCode(); method public int getEventCode(); + method public int getObjectFormatCode(); + method public int getObjectHandle(); + method public int getObjectPropCode(); + method public int getParameter1(); + method public int getParameter2(); + method public int getParameter3(); + method public int getStorageId(); + method public int getTransactionId(); } public final class MtpObjectInfo { @@ -24207,6 +24217,7 @@ package android.net { method public android.net.Network[] getAllNetworks(); method public deprecated boolean getBackgroundDataSetting(); method public android.net.Network getBoundNetworkForProcess(); + method public java.lang.String getCaptivePortalServerUrl(); method public android.net.ProxyInfo getDefaultProxy(); method public android.net.LinkProperties getLinkProperties(android.net.Network); method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network); @@ -37913,6 +37924,7 @@ package android.telephony { field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; + field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool"; field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool"; field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int"; @@ -52207,6 +52219,8 @@ package java.lang { public class InternalError extends java.lang.VirtualMachineError { ctor public InternalError(); ctor public InternalError(java.lang.String); + ctor public InternalError(java.lang.String, java.lang.Throwable); + ctor public InternalError(java.lang.Throwable); } public class InterruptedException extends java.lang.Exception { @@ -52979,6 +52993,8 @@ package java.lang { public abstract class VirtualMachineError extends java.lang.Error { ctor public VirtualMachineError(); ctor public VirtualMachineError(java.lang.String); + ctor public VirtualMachineError(java.lang.String, java.lang.Throwable); + ctor public VirtualMachineError(java.lang.Throwable); } public final class Void { @@ -56721,6 +56737,7 @@ package java.security.cert { method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); } public class CertPathBuilderException extends java.security.GeneralSecurityException { @@ -56738,6 +56755,13 @@ package java.security.cert { public abstract class CertPathBuilderSpi { ctor public CertPathBuilderSpi(); method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException; + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); + } + + public abstract interface CertPathChecker { + method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; + method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; + method public abstract boolean isForwardCheckingSupported(); } public abstract interface CertPathParameters implements java.lang.Cloneable { @@ -56752,6 +56776,7 @@ package java.security.cert { method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -56788,6 +56813,7 @@ package java.security.cert { public abstract class CertPathValidatorSpi { ctor public CertPathValidatorSpi(); + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -56946,9 +56972,10 @@ package java.security.cert { method public java.security.cert.CertPath getCertPath(); } - public abstract class PKIXCertPathChecker implements java.lang.Cloneable { + public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable { ctor protected PKIXCertPathChecker(); method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException; + method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; method public java.lang.Object clone(); method public abstract java.util.Set<java.lang.String> getSupportedExtensions(); method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; @@ -57008,6 +57035,30 @@ package java.security.cert { enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT; } + public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker { + ctor protected PKIXRevocationChecker(); + method public java.util.List<java.security.cert.Extension> getOcspExtensions(); + method public java.net.URI getOcspResponder(); + method public java.security.cert.X509Certificate getOcspResponderCert(); + method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses(); + method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions(); + method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions(); + method public void setOcspExtensions(java.util.List<java.security.cert.Extension>); + method public void setOcspResponder(java.net.URI); + method public void setOcspResponderCert(java.security.cert.X509Certificate); + method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>); + method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>); + } + + public static final class PKIXRevocationChecker.Option extends java.lang.Enum { + method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String); + method public static final java.security.cert.PKIXRevocationChecker.Option[] values(); + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL; + } + public abstract interface PolicyNode { method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren(); method public abstract int getDepth(); @@ -57167,6 +57218,7 @@ package java.security.cert { method public javax.security.auth.x500.X500Principal getSubjectX500Principal(); method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException; method public abstract int getVersion(); + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract interface X509Extension { diff --git a/api/test-current.txt b/api/test-current.txt index 13d98f49b0e8..21b101fef449 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5795,6 +5795,7 @@ package android.app.admin { method public boolean getCameraDisabled(android.content.ComponentName); method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException; method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName); + method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); method public java.lang.String getDeviceOwnerLockScreenInfo(); @@ -5859,6 +5860,7 @@ package android.app.admin { method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); + method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); @@ -12792,6 +12794,7 @@ package android.graphics.drawable { method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable); field public static final int PADDING_MODE_NEST = 0; // 0x0 field public static final int PADDING_MODE_STACK = 1; // 0x1 + field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000 } public class LevelListDrawable extends android.graphics.drawable.DrawableContainer { @@ -20376,8 +20379,6 @@ package android.media { field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608"; field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt"; field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc"; - field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc"; field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp"; field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc"; field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; @@ -22587,7 +22588,16 @@ package android.mtp { public class MtpEvent { ctor public MtpEvent(); + method public int getDevicePropCode(); method public int getEventCode(); + method public int getObjectFormatCode(); + method public int getObjectHandle(); + method public int getObjectPropCode(); + method public int getParameter1(); + method public int getParameter2(); + method public int getParameter3(); + method public int getStorageId(); + method public int getTransactionId(); } public final class MtpObjectInfo { @@ -49558,6 +49568,8 @@ package java.lang { public class InternalError extends java.lang.VirtualMachineError { ctor public InternalError(); ctor public InternalError(java.lang.String); + ctor public InternalError(java.lang.String, java.lang.Throwable); + ctor public InternalError(java.lang.Throwable); } public class InterruptedException extends java.lang.Exception { @@ -50330,6 +50342,8 @@ package java.lang { public abstract class VirtualMachineError extends java.lang.Error { ctor public VirtualMachineError(); ctor public VirtualMachineError(java.lang.String); + ctor public VirtualMachineError(java.lang.String, java.lang.Throwable); + ctor public VirtualMachineError(java.lang.Throwable); } public final class Void { @@ -54072,6 +54086,7 @@ package java.security.cert { method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); } public class CertPathBuilderException extends java.security.GeneralSecurityException { @@ -54089,6 +54104,13 @@ package java.security.cert { public abstract class CertPathBuilderSpi { ctor public CertPathBuilderSpi(); method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException; + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); + } + + public abstract interface CertPathChecker { + method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; + method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; + method public abstract boolean isForwardCheckingSupported(); } public abstract interface CertPathParameters implements java.lang.Cloneable { @@ -54103,6 +54125,7 @@ package java.security.cert { method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException; method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException; method public final java.security.Provider getProvider(); + method public final java.security.cert.CertPathChecker getRevocationChecker(); method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -54139,6 +54162,7 @@ package java.security.cert { public abstract class CertPathValidatorSpi { ctor public CertPathValidatorSpi(); + method public java.security.cert.CertPathChecker engineGetRevocationChecker(); method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException; } @@ -54297,9 +54321,10 @@ package java.security.cert { method public java.security.cert.CertPath getCertPath(); } - public abstract class PKIXCertPathChecker implements java.lang.Cloneable { + public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable { ctor protected PKIXCertPathChecker(); method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException; + method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException; method public java.lang.Object clone(); method public abstract java.util.Set<java.lang.String> getSupportedExtensions(); method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException; @@ -54359,6 +54384,30 @@ package java.security.cert { enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT; } + public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker { + ctor protected PKIXRevocationChecker(); + method public java.util.List<java.security.cert.Extension> getOcspExtensions(); + method public java.net.URI getOcspResponder(); + method public java.security.cert.X509Certificate getOcspResponderCert(); + method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses(); + method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions(); + method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions(); + method public void setOcspExtensions(java.util.List<java.security.cert.Extension>); + method public void setOcspResponder(java.net.URI); + method public void setOcspResponderCert(java.security.cert.X509Certificate); + method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>); + method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>); + } + + public static final class PKIXRevocationChecker.Option extends java.lang.Enum { + method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String); + method public static final java.security.cert.PKIXRevocationChecker.Option[] values(); + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS; + enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL; + } + public abstract interface PolicyNode { method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren(); method public abstract int getDepth(); @@ -54518,6 +54567,7 @@ package java.security.cert { method public javax.security.auth.x500.X500Principal getSubjectX500Principal(); method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException; method public abstract int getVersion(); + method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException; } public abstract interface X509Extension { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9540ae1cfbf3..2175a9eed90f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -592,6 +592,14 @@ public class ActivityManager { public static boolean isAlwaysOnTop(int stackId) { return stackId == PINNED_STACK_ID; } + + /** + * Returns true if the top task in the task is allowed to return home when finished and + * there are other tasks in the stack. + */ + public static boolean allowTopTaskToReturnHome(int stackId) { + return stackId != PINNED_STACK_ID; + } } /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 81e00ff80e11..4e55c89fa739 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2015,7 +2015,7 @@ public final class ActivityThread { private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s"; // Formatting for checkin service - update version if row format changes - private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; + private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4; static void printRow(PrintWriter pw, String format, Object...objs) { pw.println(String.format(format, objs)); @@ -2091,6 +2091,25 @@ public final class ActivityThread { pw.print(memInfo.otherPrivateClean); pw.print(','); pw.print(memInfo.getTotalPrivateClean()); pw.print(','); + // Heap info - swapped out + pw.print(memInfo.nativeSwappedOut); pw.print(','); + pw.print(memInfo.dalvikSwappedOut); pw.print(','); + pw.print(memInfo.otherSwappedOut); pw.print(','); + pw.print(memInfo.getTotalSwappedOut()); pw.print(','); + + // Heap info - swapped out pss + if (memInfo.hasSwappedOutPss) { + pw.print(memInfo.nativeSwappedOutPss); pw.print(','); + pw.print(memInfo.dalvikSwappedOutPss); pw.print(','); + pw.print(memInfo.otherSwappedOutPss); pw.print(','); + pw.print(memInfo.getTotalSwappedOutPss()); pw.print(','); + } else { + pw.print("N/A,"); + pw.print("N/A,"); + pw.print("N/A,"); + pw.print("N/A,"); + } + // Heap info - other areas for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); @@ -2100,6 +2119,12 @@ public final class ActivityThread { pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); + pw.print(memInfo.getOtherSwappedOut(i)); pw.print(','); + if (memInfo.hasSwappedOutPss) { + pw.print(memInfo.getOtherSwappedOutPss(i)); pw.print(','); + } else { + pw.print("N/A,"); + } } return; } @@ -2107,35 +2132,44 @@ public final class ActivityThread { if (!dumpSummaryOnly) { if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", - "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); + "Shared", "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap", + "Heap", "Heap", "Heap"); printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", - "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); + "Clean", "Clean", "Dirty", + "Size", "Alloc", "Free"); printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", "------", "------", "------", "------", "------", "------"); printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, memInfo.nativePrivateDirty, memInfo.nativeSharedClean, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ? + memInfo.nativeSwappedOut : memInfo.nativeSwappedOutPss, nativeMax, nativeAllocated, nativeFree); printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ? + memInfo.dalvikSwappedOut : memInfo.dalvikSwappedOutPss, dalvikMax, dalvikAllocated, dalvikFree); } else { printRow(pw, HEAP_COLUMN, "", "Pss", "Private", - "Private", "Swapped", "Heap", "Heap", "Heap"); + "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap", + "Heap", "Heap", "Heap"); printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", "Clean", "Dirty", "Size", "Alloc", "Free"); printRow(pw, HEAP_COLUMN, "", "------", "------", "------", "------", "------", "------", "------", "------"); printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, memInfo.nativePrivateDirty, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + memInfo.nativePrivateClean, + memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss : + memInfo.nativeSwappedOut, nativeMax, nativeAllocated, nativeFree); printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, memInfo.dalvikPrivateDirty, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + memInfo.dalvikPrivateClean, + memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss : + memInfo.dalvikSwappedOut, dalvikMax, dalvikAllocated, dalvikFree); } @@ -2146,6 +2180,7 @@ public final class ActivityThread { int otherSharedClean = memInfo.otherSharedClean; int otherPrivateClean = memInfo.otherPrivateClean; int otherSwappedOut = memInfo.otherSwappedOut; + int otherSwappedOutPss = memInfo.otherSwappedOutPss; for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { final int myPss = memInfo.getOtherPss(i); @@ -2155,16 +2190,22 @@ public final class ActivityThread { final int mySharedClean = memInfo.getOtherSharedClean(i); final int myPrivateClean = memInfo.getOtherPrivateClean(i); final int mySwappedOut = memInfo.getOtherSwappedOut(i); + final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { + || mySharedClean != 0 || myPrivateClean != 0 + || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + mySharedClean, myPrivateClean, + memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, + "", "", ""); } else { printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); + myPrivateClean, + memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, + "", "", ""); } otherPss -= myPss; otherSwappablePss -= mySwappablePss; @@ -2173,26 +2214,32 @@ public final class ActivityThread { otherSharedClean -= mySharedClean; otherPrivateClean -= myPrivateClean; otherSwappedOut -= mySwappedOut; + otherSwappedOutPss -= mySwappedOutPss; } } if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, - otherSwappedOut, "", "", ""); + memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut, + "", "", ""); printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalSwappablePss(), memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() : + memInfo.getTotalSwappedOutPss(), + nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, + nativeFree+dalvikFree); } else { printRow(pw, HEAP_COLUMN, "Unknown", otherPss, - otherPrivateDirty, otherPrivateClean, otherSwappedOut, + otherPrivateDirty, otherPrivateClean, + memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut, "", "", ""); printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), memInfo.getTotalPrivateDirty(), memInfo.getTotalPrivateClean(), + memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() : memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); @@ -2211,16 +2258,22 @@ public final class ActivityThread { final int mySharedClean = memInfo.getOtherSharedClean(i); final int myPrivateClean = memInfo.getOtherPrivateClean(i); final int mySwappedOut = memInfo.getOtherSwappedOut(i); + final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0) { + || mySharedClean != 0 || myPrivateClean != 0 + || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + mySharedClean, myPrivateClean, + memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, + "", "", ""); } else { printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); + myPrivateClean, + memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, + "", "", ""); } } } @@ -2246,9 +2299,15 @@ public final class ActivityThread { printRow(pw, ONE_COUNT_COLUMN, "System:", memInfo.getSummarySystem()); pw.println(" "); - printRow(pw, TWO_COUNT_COLUMNS, - "TOTAL:", memInfo.getSummaryTotalPss(), - "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap()); + if (memInfo.hasSwappedOutPss) { + printRow(pw, TWO_COUNT_COLUMNS, + "TOTAL:", memInfo.getSummaryTotalPss(), + "TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss()); + } else { + printRow(pw, TWO_COUNT_COLUMNS, + "TOTAL:", memInfo.getSummaryTotalPss(), + "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap()); + } } public void registerOnActivityPausedListener(Activity activity, diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index a73ad09d5f55..e163b1c2ad23 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -409,9 +409,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // If set this fragment is being removed from its activity. boolean mRemoving; - // True if the fragment is in the resumed state. - boolean mResumed; - // Set to true if this fragment was instantiated from a layout file. boolean mFromLayout; @@ -928,7 +925,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene * for the duration of {@link #onResume()} and {@link #onPause()} as well. */ final public boolean isResumed() { - return mResumed; + return mState >= RESUMED; } /** @@ -1630,7 +1627,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mWho = null; mAdded = false; mRemoving = false; - mResumed = false; mFromLayout = false; mInLayout = false; mRestored = false; @@ -2113,7 +2109,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene writer.print(" mBackStackNesting="); writer.println(mBackStackNesting); writer.print(prefix); writer.print("mAdded="); writer.print(mAdded); writer.print(" mRemoving="); writer.print(mRemoving); - writer.print(" mResumed="); writer.print(mResumed); writer.print(" mFromLayout="); writer.print(mFromLayout); writer.print(" mInLayout="); writer.println(mInLayout); writer.print(prefix); writer.print("mHidden="); writer.print(mHidden); @@ -2208,6 +2203,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.noteStateNotSaved(); } + mState = CREATED; mCalled = false; onCreate(savedInstanceState); if (!mCalled) { @@ -2238,6 +2234,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.noteStateNotSaved(); } + mState = ACTIVITY_CREATED; mCalled = false; onActivityCreated(savedInstanceState); if (!mCalled) { @@ -2254,6 +2251,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mChildFragmentManager.noteStateNotSaved(); mChildFragmentManager.execPendingActions(); } + mState = STARTED; mCalled = false; onStart(); if (!mCalled) { @@ -2273,6 +2271,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mChildFragmentManager.noteStateNotSaved(); mChildFragmentManager.execPendingActions(); } + mState = RESUMED; mCalled = false; onResume(); if (!mCalled) { @@ -2389,6 +2388,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.dispatchPause(); } + mState = STARTED; mCalled = false; onPause(); if (!mCalled) { @@ -2401,6 +2401,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.dispatchStop(); } + mState = STOPPED; mCalled = false; onStop(); if (!mCalled) { @@ -2428,6 +2429,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.dispatchDestroyView(); } + mState = CREATED; mCalled = false; onDestroyView(); if (!mCalled) { @@ -2443,6 +2445,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene if (mChildFragmentManager != null) { mChildFragmentManager.dispatchDestroy(); } + mState = INITIALIZING; mCalled = false; onDestroy(); if (!mCalled) { diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 696ccdb207c1..84ae09d7e988 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1004,7 +1004,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate case Fragment.STARTED: if (newState > Fragment.STARTED) { if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f); - f.mResumed = true; f.performResume(); // Get rid of this in case we saved it and never needed it. f.mSavedFragmentState = null; @@ -1017,7 +1016,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (newState < Fragment.RESUMED) { if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f); f.performPause(); - f.mResumed = false; } case Fragment.STARTED: if (newState < Fragment.STARTED) { @@ -1096,6 +1094,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); if (!f.mRetaining) { f.performDestroy(); + } else { + f.mState = Fragment.INITIALIZING; } f.mCalled = false; @@ -1119,7 +1119,11 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } - f.mState = newState; + if (f.mState != newState) { + Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; " + + "expected state " + newState + " found " + f.mState); + f.mState = newState; + } } void moveToState(Fragment f) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 08e9b1e897af..4de3ceb4792e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3540,6 +3540,66 @@ public class DevicePolicyManager { } /** + * Called by a profile owner of a managed profile to set whether contacts search from + * the managed profile will be shown in the parent profile, for incoming calls. + * + * <p>The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param disabled If true contacts search in the managed profile is not displayed. + */ + public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin, + boolean disabled) { + if (mService != null) { + try { + mService.setCrossProfileContactsSearchDisabled(admin, disabled); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + } + + /** + * Called by a profile owner of a managed profile to determine whether or not contacts search + * has been disabled. + * + * <p>The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + */ + public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) { + if (mService != null) { + try { + return mService.getCrossProfileContactsSearchDisabled(admin); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + + /** + * Determine whether or not contacts search has been disabled. + * + * @param userHandle The user for whom to check the contacts search permission + * @hide + */ + public boolean getCrossProfileContactsSearchDisabled(@NonNull UserHandle userHandle) { + if (mService != null) { + try { + return mService + .getCrossProfileContactsSearchDisabledForUser(userHandle.getIdentifier()); + } catch (RemoteException e) { + Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e); + } + } + return false; + } + + /** * Start Quick Contact on the managed profile for the user, if the policy allows. * @hide */ diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 754cb432bf7c..d3c32c51798a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -202,6 +202,9 @@ interface IDevicePolicyManager { void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled); boolean getCrossProfileCallerIdDisabled(in ComponentName who); boolean getCrossProfileCallerIdDisabledForUser(int userId); + void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled); + boolean getCrossProfileContactsSearchDisabled(in ComponentName who); + boolean getCrossProfileContactsSearchDisabledForUser(int userId); void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent); void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 38abac7e741b..5113e1979812 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4413,6 +4413,15 @@ public abstract class PackageManager { final PackageParser parser = new PackageParser(); final File apkFile = new File(archiveFilePath); try { + if ((flags & (MATCH_ENCRYPTION_UNAWARE | MATCH_ENCRYPTION_AWARE)) != 0) { + // Caller expressed an explicit opinion about what encryption + // aware/unaware components they want to see, so fall through and + // give them what they want + } else { + // Caller expressed no opinion, so match everything + flags |= MATCH_ENCRYPTION_AWARE_AND_UNAWARE; + } + PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0); if ((flags & GET_SIGNATURES) != 0) { parser.collectCertificates(pkg, 0); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 134966248f94..a0df610c7428 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -463,92 +463,60 @@ public class PackageParser { p.featureGroups.toArray(pi.featureGroups); } } - if ((flags&PackageManager.GET_ACTIVITIES) != 0) { - int N = p.activities.size(); + if ((flags & PackageManager.GET_ACTIVITIES) != 0) { + final int N = p.activities.size(); if (N > 0) { - if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.activities = new ActivityInfo[N]; - } else { - int num = 0; - for (int i=0; i<N; i++) { - if (p.activities.get(i).info.enabled) num++; - } - pi.activities = new ActivityInfo[num]; - } - for (int i=0, j=0; i<N; i++) { - final Activity activity = p.activities.get(i); - if (activity.info.enabled - || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags, - state, userId); + int num = 0; + final ActivityInfo[] res = new ActivityInfo[N]; + for (int i = 0; i < N; i++) { + final Activity a = p.activities.get(i); + if (state.isMatch(a.info, flags)) { + res[num++] = generateActivityInfo(a, flags, state, userId); } } + pi.activities = ArrayUtils.trimToSize(res, num); } } - if ((flags&PackageManager.GET_RECEIVERS) != 0) { - int N = p.receivers.size(); + if ((flags & PackageManager.GET_RECEIVERS) != 0) { + final int N = p.receivers.size(); if (N > 0) { - if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.receivers = new ActivityInfo[N]; - } else { - int num = 0; - for (int i=0; i<N; i++) { - if (p.receivers.get(i).info.enabled) num++; - } - pi.receivers = new ActivityInfo[num]; - } - for (int i=0, j=0; i<N; i++) { - final Activity activity = p.receivers.get(i); - if (activity.info.enabled - || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, - state, userId); + int num = 0; + final ActivityInfo[] res = new ActivityInfo[N]; + for (int i = 0; i < N; i++) { + final Activity a = p.receivers.get(i); + if (state.isMatch(a.info, flags)) { + res[num++] = generateActivityInfo(a, flags, state, userId); } } + pi.receivers = ArrayUtils.trimToSize(res, num); } } - if ((flags&PackageManager.GET_SERVICES) != 0) { - int N = p.services.size(); + if ((flags & PackageManager.GET_SERVICES) != 0) { + final int N = p.services.size(); if (N > 0) { - if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.services = new ServiceInfo[N]; - } else { - int num = 0; - for (int i=0; i<N; i++) { - if (p.services.get(i).info.enabled) num++; - } - pi.services = new ServiceInfo[num]; - } - for (int i=0, j=0; i<N; i++) { - final Service service = p.services.get(i); - if (service.info.enabled - || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.services[j++] = generateServiceInfo(p.services.get(i), flags, - state, userId); + int num = 0; + final ServiceInfo[] res = new ServiceInfo[N]; + for (int i = 0; i < N; i++) { + final Service s = p.services.get(i); + if (state.isMatch(s.info, flags)) { + res[num++] = generateServiceInfo(s, flags, state, userId); } } + pi.services = ArrayUtils.trimToSize(res, num); } } - if ((flags&PackageManager.GET_PROVIDERS) != 0) { - int N = p.providers.size(); + if ((flags & PackageManager.GET_PROVIDERS) != 0) { + final int N = p.providers.size(); if (N > 0) { - if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.providers = new ProviderInfo[N]; - } else { - int num = 0; - for (int i=0; i<N; i++) { - if (p.providers.get(i).info.enabled) num++; - } - pi.providers = new ProviderInfo[num]; - } - for (int i=0, j=0; i<N; i++) { - final Provider provider = p.providers.get(i); - if (provider.info.enabled - || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) { - pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, - state, userId); + int num = 0; + final ProviderInfo[] res = new ProviderInfo[N]; + for (int i = 0; i < N; i++) { + final Provider pr = p.providers.get(i); + if (state.isMatch(pr.info, flags)) { + res[num++] = generateProviderInfo(pr, flags, state, userId); } } + pi.providers = ArrayUtils.trimToSize(res, num); } } if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) { diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 91fdf7f71f10..38e0044ea74c 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -17,9 +17,20 @@ package android.content.pm; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; +import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; +import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE; +import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE; +import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import android.util.ArraySet; +import com.android.internal.util.ArrayUtils; + /** * Per-user state information about a package. * @hide @@ -58,12 +69,77 @@ public class PackageUserState { hidden = o.hidden; suspended = o.suspended; lastDisableAppCaller = o.lastDisableAppCaller; - disabledComponents = o.disabledComponents != null - ? new ArraySet<>(o.disabledComponents) : null; - enabledComponents = o.enabledComponents != null - ? new ArraySet<>(o.enabledComponents) : null; + disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); + enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); blockUninstall = o.blockUninstall; domainVerificationStatus = o.domainVerificationStatus; appLinkGeneration = o.appLinkGeneration; } + + /** + * Test if this package is installed. + */ + public boolean isInstalled(int flags) { + return (this.installed && !this.hidden) + || (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0; + } + + /** + * Test if the given component is considered installed, enabled and a match + * for the given flags. + */ + public boolean isMatch(ComponentInfo componentInfo, int flags) { + if (!isInstalled(flags)) return false; + if (!isEnabled(componentInfo, flags)) return false; + + if ((flags & MATCH_SYSTEM_ONLY) != 0) { + if (!componentInfo.applicationInfo.isSystemApp()) { + return false; + } + } + + final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0) + && !componentInfo.encryptionAware; + final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0) + && componentInfo.encryptionAware; + return matchesUnaware || matchesAware; + } + + /** + * Test if the given component is considered enabled. + */ + public boolean isEnabled(ComponentInfo componentInfo, int flags) { + if ((flags & MATCH_DISABLED_COMPONENTS) != 0) { + return true; + } + + // First check if the overall package is disabled; if the package is + // enabled then fall through to check specific component + switch (this.enabled) { + case COMPONENT_ENABLED_STATE_DISABLED: + case COMPONENT_ENABLED_STATE_DISABLED_USER: + return false; + case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: + if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) { + return false; + } + case COMPONENT_ENABLED_STATE_DEFAULT: + if (!componentInfo.applicationInfo.enabled) { + return false; + } + case COMPONENT_ENABLED_STATE_ENABLED: + break; + } + + // Check if component has explicit state before falling through to + // the manifest default + if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) { + return true; + } + if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) { + return false; + } + + return componentInfo.enabled; + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f642f08df76e..282688295fee 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1258,6 +1258,10 @@ public class InputMethodService extends AbstractInputMethodService { */ @CallSuper public boolean onEvaluateInputViewShown() { + if (mSettingsObserver == null) { + Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here."); + return false; + } if (mSettingsObserver.shouldShowImeWithHardKeyboard()) { return true; } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index cabc6fabc355..176e923c81b2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -897,6 +898,24 @@ public class ConnectivityManager { } /** + * Gets the URL that should be used for resolving whether a captive portal is present. + * 1. This URL should respond with a 204 response to a GET request to indicate no captive + * portal is present. + * 2. This URL must be HTTP as redirect responses are used to find captive portal + * sign-in pages. Captive portals cannot respond to HTTPS requests with redirects. + * + * @hide + */ + @SystemApi + public String getCaptivePortalServerUrl() { + try { + return mService.getCaptivePortalServerUrl(); + } catch (RemoteException e) { + return null; + } + } + + /** * Tells the underlying networking system that the caller wants to * begin using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d4dd6692110a..ef911373d4c1 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -165,4 +165,6 @@ interface IConnectivityManager in IBinder binder, String srcAddr, int srcPort, String dstAddr); void stopKeepalive(in Network network, int slot); + + String getCaptivePortalServerUrl(); } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index fdd34f586e8c..e58744bfd905 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -130,6 +130,9 @@ public final class Debug /** The dirty dalvik pages that have been swapped out. */ /** @hide We may want to expose this, eventually. */ public int dalvikSwappedOut; + /** The dirty dalvik pages that have been swapped out, proportional. */ + /** @hide We may want to expose this, eventually. */ + public int dalvikSwappedOutPss; /** The proportional set size for the native heap. */ public int nativePss; @@ -149,6 +152,9 @@ public final class Debug /** The dirty native pages that have been swapped out. */ /** @hide We may want to expose this, eventually. */ public int nativeSwappedOut; + /** The dirty native pages that have been swapped out, proportional. */ + /** @hide We may want to expose this, eventually. */ + public int nativeSwappedOutPss; /** The proportional set size for everything else. */ public int otherPss; @@ -168,6 +174,13 @@ public final class Debug /** The dirty pages used by anyting else that have been swapped out. */ /** @hide We may want to expose this, eventually. */ public int otherSwappedOut; + /** The dirty pages used by anyting else that have been swapped out, proportional. */ + /** @hide We may want to expose this, eventually. */ + public int otherSwappedOutPss; + + /** Whether the kernel reports proportional swap usage */ + /** @hide */ + public boolean hasSwappedOutPss; /** @hide */ public static final int HEAP_UNKNOWN = 0; @@ -235,7 +248,7 @@ public final class Debug public static final int NUM_DVK_STATS = 8; /** @hide */ - public static final int NUM_CATEGORIES = 7; + public static final int NUM_CATEGORIES = 8; /** @hide */ public static final int offsetPss = 0; @@ -251,6 +264,8 @@ public final class Debug public static final int offsetSharedClean = 5; /** @hide */ public static final int offsetSwappedOut = 6; + /** @hide */ + public static final int offsetSwappedOutPss = 7; private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES]; @@ -261,7 +276,7 @@ public final class Debug * Return total PSS memory usage in kB. */ public int getTotalPss() { - return dalvikPss + nativePss + otherPss; + return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss(); } /** @@ -274,7 +289,8 @@ public final class Debug } /** - * Return total PSS memory usage in kB. + * Return total PSS memory usage in kB mapping a file of one of the following extension: + * .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art . */ public int getTotalSwappablePss() { return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss; @@ -316,6 +332,14 @@ public final class Debug return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut; } + /** + * Return total swapped out memory in kB, proportional. + * @hide + */ + public int getTotalSwappedOutPss() { + return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss; + } + /** @hide */ public int getOtherPss(int which) { return otherStats[which*NUM_CATEGORIES + offsetPss]; @@ -359,6 +383,11 @@ public final class Debug } /** @hide */ + public int getOtherSwappedOutPss(int which) { + return otherStats[which*NUM_CATEGORIES + offsetSwappedOutPss]; + } + + /** @hide */ public static String getOtherLabel(int which) { switch (which) { case OTHER_DALVIK_OTHER: return "Dalvik Other"; @@ -632,12 +661,24 @@ public final class Debug * know if the Swap memory is shared or private, so we don't know * what to blame on the application and what on the system. * For now, just lump all the Swap in one place. + * For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()} + * will report the application proportional Swap. * @hide */ public int getSummaryTotalSwap() { return getTotalSwappedOut(); } + /** + * Total proportional Swap in KB. + * Notes: + * * Always 0 if {@link #hasSwappedOutPss} is false. + * @hide + */ + public int getSummaryTotalSwapPss() { + return getTotalSwappedOutPss(); + } + public int describeContents() { return 0; } @@ -664,6 +705,8 @@ public final class Debug dest.writeInt(otherPrivateClean); dest.writeInt(otherSharedClean); dest.writeInt(otherSwappedOut); + dest.writeInt(hasSwappedOutPss ? 1 : 0); + dest.writeInt(otherSwappedOutPss); dest.writeIntArray(otherStats); } @@ -689,6 +732,8 @@ public final class Debug otherPrivateClean = source.readInt(); otherSharedClean = source.readInt(); otherSwappedOut = source.readInt(); + hasSwappedOutPss = source.readInt() != 0; + otherSwappedOutPss = source.readInt(); otherStats = source.createIntArray(); } @@ -1563,11 +1608,12 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Retrieves the PSS memory used by the process as given by the - * smaps. Optionally supply a long array of 1 entry to also - * receive the uss of the process, and another array to also - * retrieve the separate memtrack size. @hide + * smaps. Optionally supply a long array of 2 entries to also + * receive the Uss and SwapPss of the process, and another array to also + * retrieve the separate memtrack size. + * @hide */ - public static native long getPss(int pid, long[] outUss, long[] outMemtrack); + public static native long getPss(int pid, long[] outUssSwapPss, long[] outMemtrack); /** @hide */ public static final int MEMINFO_TOTAL = 0; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d4c994468030..a6485e4d7e4e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -641,7 +641,7 @@ public final class Settings { * provided by the platform for applications to operate correctly in the various power * saving mode. This is only for unusual applications that need to deeply control their own * execution, at the potential expense of the user's battery life. Note that these applications - * greatly run the risk of showing to the user has how power consumers on their device.</p> + * greatly run the risk of showing to the user as high power consumers on their device.</p> * <p> * Input: The Intent's data URI must specify the application package name * to be shown, with the "package" scheme. That is "package:com.my.app". diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index c42c791769e6..2a52961984f7 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -688,13 +688,14 @@ class TextLine { * @param bottom the bottom of the line * @param fmi receives metrics information, can be null * @param needWidth true if the width of the run is needed + * @param offset the offset for the purpose of measuring * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ private float handleText(TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, Canvas c, float x, int top, int y, int bottom, - FontMetricsInt fmi, boolean needWidth) { + FontMetricsInt fmi, boolean needWidth, int offset) { // Get metrics first (even for empty strings or "0" width runs) if (fmi != null) { @@ -712,11 +713,11 @@ class TextLine { if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { if (mCharsValid) { ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, - runIsRtl, end); + runIsRtl, offset); } else { int delta = mStart; ret = wp.getRunAdvance(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, runIsRtl, delta + end); + delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); } } @@ -865,8 +866,8 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); final int mlimit = measureLimit; - return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top, - y, bottom, fmi, needWidth || mlimit < measureLimit); + return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top, + y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit); } mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); @@ -910,13 +911,14 @@ class TextLine { } for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - + jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) - mStart; + int offset = Math.min(jnext, mlimit); wp.set(mPaint); for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above - if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || + if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) || (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; CharacterStyle span = mCharacterStyleSpanSet.spans[k]; @@ -928,7 +930,7 @@ class TextLine { wp.setHyphenEdit(0); } x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, - top, y, bottom, fmi, needWidth || jnext < measureLimit); + top, y, bottom, fmi, needWidth || jnext < measureLimit, offset); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1c243929fa49..f674298d3774 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5359,6 +5359,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager void offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds) { + final RectF rectF = mAttachInfo != null ? mAttachInfo.mTmpTransformRect1 : new RectF(); + final Matrix inverse = mAttachInfo != null ? mAttachInfo.mTmpMatrix : new Matrix(); + // already in the same coord system :) if (descendant == this) { return; @@ -5372,8 +5375,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager && (theParent != this)) { if (offsetFromChildToParent) { - rect.offset(descendant.mLeft - descendant.mScrollX, - descendant.mTop - descendant.mScrollY); + rect.offset(-descendant.mScrollX, -descendant.mScrollY); + + if (!descendant.hasIdentityMatrix()) { + rectF.set(rect); + descendant.getMatrix().mapRect(rectF); + rectF.roundOut(rect); + } + + rect.offset(descendant.mLeft, descendant.mTop); + if (clipToBounds) { View p = (View) theParent; boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft, @@ -5391,8 +5402,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager rect.setEmpty(); } } - rect.offset(descendant.mScrollX - descendant.mLeft, - descendant.mScrollY - descendant.mTop); + rect.offset(-descendant.mLeft, -descendant.mTop); + + if (!descendant.hasIdentityMatrix()) { + descendant.getMatrix().invert(inverse); + rectF.set(rect); + inverse.mapRect(rectF); + rectF.roundOut(rect); + } + + rect.offset(descendant.mScrollX, descendant.mScrollY); } descendant = (View) theParent; @@ -5403,11 +5422,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // to get into our coordinate space if (theParent == this) { if (offsetFromChildToParent) { - rect.offset(descendant.mLeft - descendant.mScrollX, - descendant.mTop - descendant.mScrollY); + rect.offset(-descendant.mScrollX, -descendant.mScrollY); + + if (!descendant.hasIdentityMatrix()) { + rectF.set(rect); + descendant.getMatrix().mapRect(rectF); + rectF.roundOut(rect); + } + + rect.offset(descendant.mLeft, descendant.mTop); } else { - rect.offset(descendant.mScrollX - descendant.mLeft, - descendant.mScrollY - descendant.mTop); + rect.offset(-descendant.mLeft, -descendant.mTop); + + if (!descendant.hasIdentityMatrix()) { + descendant.getMatrix().invert(inverse); + rectF.set(rect); + inverse.mapRect(rectF); + rectF.roundOut(rect); + } + + rect.offset(descendant.mScrollX, descendant.mScrollY); } } else { throw new IllegalArgumentException("parameter must be a descendant of this view"); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0fb39516d4d2..3eb2e37be781 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1488,7 +1488,8 @@ public final class ViewRootImpl implements ViewParent, if ((lp.width == ViewGroup.LayoutParams.WRAP_CONTENT || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) && (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL - || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD)) { + || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD + || lp.type == WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)) { windowSizeMayChange = true; // NOTE -- system code, won't try to do compat mode. Point size = new Point(); diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index c79e18449e39..09cf7049c04a 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -271,6 +271,10 @@ public class Spinner extends AbsSpinner implements OnClickListener { attrs, R.styleable.Spinner, defStyleAttr, defStyleRes); mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT); + if (pa.hasValueOrEmpty(R.styleable.Spinner_dropDownSelector)) { + popup.setListSelector(pa.getDrawable( + R.styleable.Spinner_dropDownSelector)); + } popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground)); popup.setPromptText(a.getString(R.styleable.Spinner_prompt)); pa.recycle(); diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java index 830da79c2a0b..b3222f013b3e 100644 --- a/core/java/com/android/internal/os/InstallerConnection.java +++ b/core/java/com/android/internal/os/InstallerConnection.java @@ -19,6 +19,7 @@ package com.android.internal.os; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.SystemClock; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.util.Preconditions; @@ -29,6 +30,7 @@ import libcore.io.Streams; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; /** * Represents a connection to {@code installd}. Allows multiple connect and @@ -61,6 +63,11 @@ public class InstallerConnection { } public synchronized String transact(String cmd) { + if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { + Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" + + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable()); + } + if (!connect()) { Slog.e(TAG, "connection failed"); return "-1"; @@ -96,44 +103,50 @@ public class InstallerConnection { } } - public int execute(String cmd) { - if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { - Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" - + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable()); + public void execute(String cmd, Object... args) throws InstallerException { + final String resRaw = executeForResult(cmd, args); + int res = -1; + try { + res = Integer.parseInt(resRaw); + } catch (NumberFormatException ignored) { + } + if (res != 0) { + throw new InstallerException( + "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res); } + } - String res = transact(cmd); - try { - return Integer.parseInt(res); - } catch (NumberFormatException ex) { - return -1; + public String executeForResult(String cmd, Object... args) + throws InstallerException { + final StringBuilder builder = new StringBuilder(cmd); + for (Object arg : args) { + String escaped; + if (arg == null) { + escaped = ""; + } else { + escaped = String.valueOf(arg); + } + if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) { + throw new InstallerException( + "Invalid argument while executing " + cmd + " " + Arrays.toString(args)); + } + if (TextUtils.isEmpty(escaped)) { + escaped = "!"; + } + builder.append(' ').append(escaped); } + return transact(builder.toString()); } - public int dexopt(String apkPath, int uid, String instructionSet, - int dexoptNeeded, int dexFlags) { - return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, - null /*outputPath*/, dexFlags); + public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, + int dexFlags) throws InstallerException { + dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags); } - public int dexopt(String apkPath, int uid, String pkgName, String instructionSet, - int dexoptNeeded, String outputPath, int dexFlags) { - StringBuilder builder = new StringBuilder("dexopt"); - builder.append(' '); - builder.append(apkPath); - builder.append(' '); - builder.append(uid); - builder.append(' '); - builder.append(pkgName); - builder.append(' '); - builder.append(instructionSet); - builder.append(' '); - builder.append(dexoptNeeded); - builder.append(' '); - builder.append(outputPath != null ? outputPath : "!"); - builder.append(' '); - builder.append(dexFlags); - return execute(builder.toString()); + public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, + int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException { + execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath, + dexFlags); } private boolean connect() { @@ -227,11 +240,19 @@ public class InstallerConnection { public void waitForConnection() { for (;;) { - if (execute("ping") >= 0) { + try { + execute("ping"); return; + } catch (InstallerException ignored) { } Slog.w(TAG, "installd not ready"); SystemClock.sleep(1000); } } + + public static class InstallerException extends Exception { + public InstallerException(String detailMessage) { + super(detailMessage); + } + } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 4a1f7f490a6d..eecc0eeab062 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -37,6 +37,8 @@ import android.util.EventLog; import android.util.Log; import android.webkit.WebViewFactory; +import com.android.internal.os.InstallerConnection.InstallerException; + import dalvik.system.DexFile; import dalvik.system.PathClassLoader; import dalvik.system.VMRuntime; @@ -502,8 +504,8 @@ public class ZygoteInit { dexoptNeeded, 0 /*dexFlags*/); } } - } catch (IOException ioe) { - throw new RuntimeException("Error starting system_server", ioe); + } catch (IOException | InstallerException e) { + throw new RuntimeException("Error starting system_server", e); } finally { installer.disconnect(); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index f159a4d71f20..b4c4ef506f46 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -2677,20 +2677,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { invalidatePanelMenu(FEATURE_ACTION_BAR); } } else { - mTitleView = (TextView)findViewById(R.id.title); + mTitleView = (TextView) findViewById(R.id.title); if (mTitleView != null) { - mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { - View titleContainer = findViewById( - R.id.title_container); + final View titleContainer = findViewById(R.id.title_container); if (titleContainer != null) { titleContainer.setVisibility(View.GONE); } else { mTitleView.setVisibility(View.GONE); } - if (mContentParent instanceof FrameLayout) { - ((FrameLayout)mContentParent).setForeground(null); - } + mContentParent.setForeground(null); } else { mTitleView.setText(mTitle); } diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 16bf9dd2900b..ca1334c6e6cc 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -26,6 +26,7 @@ import libcore.util.EmptyArray; import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; /** @@ -372,6 +373,10 @@ public class ArrayUtils { return (array != null) ? array.clone() : null; } + public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) { + return (array != null) ? new ArraySet<T>(array) : null; + } + public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) { if (cur == null) { cur = new ArraySet<>(); @@ -420,6 +425,16 @@ public class ArrayUtils { return (cur != null) ? cur.contains(val) : false; } + public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) { + if (array == null || size == 0) { + return null; + } else if (array.length == size) { + return array; + } else { + return Arrays.copyOf(array, size); + } + } + /** * Returns true if the two ArrayLists are equal with respect to the objects they contain. * The objects must be in the same order and be reference equal (== not .equals()). diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 799ed835197d..fda0ffa8bbe5 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -60,19 +60,7 @@ static jlong Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle, j // as all the data needed is contained within the newly created LocalMatrixShader. SkASSERT(shaderHandle); SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle)); - - SkMatrix currentMatrix; - SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(¤tMatrix)); - if (baseShader.get()) { - // if the matrices are same then there is no need to allocate a new - // shader that is identical to the existing one. - if (currentMatrix == *matrix) { - return reinterpret_cast<jlong>(currentShader.detach()); - } - return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(baseShader, *matrix)); - } - - return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(currentShader, *matrix)); + return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix)); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e162810fd802..8e8f6c371204 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -152,7 +152,8 @@ static struct { static struct { jmethodID postDynPolicyEventFromNative; -} gDynPolicyEventHandlerMethods; + jmethodID postRecordConfigEventFromNative; +} gAudioPolicyEventHandlerMethods; static Mutex gLock; @@ -378,12 +379,26 @@ android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val) const char* zechars = regId.string(); jstring zestring = env->NewStringUTF(zechars); - env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative, + env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative, event, zestring, val); env->ReleaseStringUTFChars(zestring, zechars); env->DeleteLocalRef(clazz); +} + +static void +android_media_AudioSystem_recording_callback(int event, int session, int source) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + jclass clazz = env->FindClass(kClassPathName); + env->CallStaticVoidMethod(clazz, + gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative, + event, session, source); + env->DeleteLocalRef(clazz); } static jint @@ -1503,6 +1518,12 @@ android_media_AudioSystem_registerDynPolicyCallback(JNIEnv *env, jobject thiz) AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback); } +static void +android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz) +{ + AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback); +} + static jint convertAudioMixToNative(JNIEnv *env, AudioMix *nAudioMix, @@ -1677,6 +1698,8 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_AudioSystem_registerPolicyMixes}, {"native_register_dynamic_policy_callback", "()V", (void *)android_media_AudioSystem_registerDynPolicyCallback}, + {"native_register_recording_callback", "()V", + (void *)android_media_AudioSystem_registerRecordingCallback}, {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, }; @@ -1780,9 +1803,12 @@ int register_android_media_AudioSystem(JNIEnv *env) gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env, eventHandlerClass, "mJniCallback", "J"); - gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative = + gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative = GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), "dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V"); + gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative = + GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName), + "recordingCallbackFromNative", "(III)V"); jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix"); gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass); diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 2488111247a2..03a1e718d2ea 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -84,6 +84,7 @@ struct stat_fields { jfieldID privateClean_field; jfieldID sharedClean_field; jfieldID swappedOut_field; + jfieldID swappedOutPss_field; }; struct stat_field_names { @@ -94,20 +95,22 @@ struct stat_field_names { const char* privateClean_name; const char* sharedClean_name; const char* swappedOut_name; + const char* swappedOutPss_name; }; static stat_fields stat_fields[_NUM_CORE_HEAP]; static stat_field_names stat_field_names[_NUM_CORE_HEAP] = { { "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty", - "otherPrivateClean", "otherSharedClean", "otherSwappedOut" }, + "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" }, { "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty", - "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut" }, + "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" }, { "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty", - "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut" } + "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" } }; jfieldID otherStats_field; +jfieldID hasSwappedOutPss_field; static bool memtrackLoaded; @@ -119,6 +122,7 @@ struct stats_t { int privateClean; int sharedClean; int swappedOut; + int swappedOutPss; }; #define BINDER_STATS "/proc/binder/stats" @@ -206,7 +210,7 @@ static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_me return err; } -static void read_mapinfo(FILE *fp, stats_t* stats) +static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss) { char line[1024]; int len, nameLen; @@ -216,7 +220,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) float sharing_proportion = 0.0; unsigned shared_clean = 0, shared_dirty = 0; unsigned private_clean = 0, private_dirty = 0; - unsigned swapped_out = 0; + unsigned swapped_out = 0, swapped_out_pss = 0; bool is_swappable = false; unsigned temp; @@ -230,6 +234,8 @@ static void read_mapinfo(FILE *fp, stats_t* stats) int subHeap = HEAP_UNKNOWN; int prevHeap = HEAP_UNKNOWN; + *foundSwapPss = false; + if(fgets(line, sizeof(line), fp) == 0) return; while (!done) { @@ -340,6 +346,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) private_clean = 0; private_dirty = 0; swapped_out = 0; + swapped_out_pss = 0; while (true) { if (fgets(line, 1024, fp) == 0) { @@ -365,6 +372,9 @@ static void read_mapinfo(FILE *fp, stats_t* stats) /* referenced = temp; */ } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) { swapped_out = temp; + } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) { + *foundSwapPss = true; + swapped_out_pss = temp; } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) { // looks like a new mapping // example: "10000000-10001000 ---p 10000000 00:00 0" @@ -390,6 +400,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) stats[whichHeap].privateClean += private_clean; stats[whichHeap].sharedClean += shared_clean; stats[whichHeap].swappedOut += swapped_out; + stats[whichHeap].swappedOutPss += swapped_out_pss; if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) { stats[subHeap].pss += pss; stats[subHeap].swappablePss += swappable_pss; @@ -398,12 +409,13 @@ static void read_mapinfo(FILE *fp, stats_t* stats) stats[subHeap].privateClean += private_clean; stats[subHeap].sharedClean += shared_clean; stats[subHeap].swappedOut += swapped_out; + stats[subHeap].swappedOutPss += swapped_out_pss; } } } } -static void load_maps(int pid, stats_t* stats) +static void load_maps(int pid, stats_t* stats, bool* foundSwapPss) { char tmp[128]; FILE *fp; @@ -412,17 +424,18 @@ static void load_maps(int pid, stats_t* stats) fp = fopen(tmp, "r"); if (fp == 0) return; - read_mapinfo(fp, stats); + read_mapinfo(fp, stats, foundSwapPss); fclose(fp); } static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, jint pid, jobject object) { + bool foundSwapPss; stats_t stats[_NUM_HEAP]; memset(&stats, 0, sizeof(stats)); - load_maps(pid, stats); + load_maps(pid, stats, &foundSwapPss); struct graphics_memory_pss graphics_mem; if (read_memtrack_memory(pid, &graphics_mem) == 0) { @@ -442,6 +455,7 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean; stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean; stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut; + stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss; } for (int i=0; i<_NUM_CORE_HEAP; i++) { @@ -452,9 +466,11 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean); env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean); env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut); + env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss); } + env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss); jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field); jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0); @@ -471,6 +487,7 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, otherArray[j++] = stats[i].privateClean; otherArray[j++] = stats[i].sharedClean; otherArray[j++] = stats[i].swappedOut; + otherArray[j++] = stats[i].swappedOutPss; } env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0); @@ -481,11 +498,12 @@ static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject o android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object); } -static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss, - jlongArray outMemtrack) +static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, + jlongArray outUssSwapPss, jlongArray outMemtrack) { char line[1024]; jlong pss = 0; + jlong swapPss = 0; jlong uss = 0; jlong memtrack = 0; @@ -521,19 +539,31 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jl } uss += atoi(c); } + } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) { + char* c = line + 8; + jlong lSwapPss; + while (*c != 0 && (*c < '0' || *c > '9')) { + c++; + } + lSwapPss = atoi(c); + swapPss += lSwapPss; + pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP } } fclose(fp); } - if (outUss != NULL) { - if (env->GetArrayLength(outUss) >= 1) { - jlong* outUssArray = env->GetLongArrayElements(outUss, 0); - if (outUssArray != NULL) { - outUssArray[0] = uss; + if (outUssSwapPss != NULL) { + if (env->GetArrayLength(outUssSwapPss) >= 1) { + jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0); + if (outUssSwapPssArray != NULL) { + outUssSwapPssArray[0] = uss; + if (env->GetArrayLength(outUssSwapPss) >= 2) { + outUssSwapPssArray[1] = swapPss; + } } - env->ReleaseLongArrayElements(outUss, outUssArray, 0); + env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0); } } @@ -1056,6 +1086,7 @@ int register_android_os_Debug(JNIEnv *env) } otherStats_field = env->GetFieldID(clazz, "otherStats", "[I"); + hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z"); for (int i=0; i<_NUM_CORE_HEAP; i++) { stat_fields[i].pss_field = @@ -1072,6 +1103,8 @@ int register_android_os_Debug(JNIEnv *env) env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I"); stat_fields[i].swappedOut_field = env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I"); + stat_fields[i].swappedOutPss_field = + env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I"); } return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods)); diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 651b45334ee0..e2150c02c0e6 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -82,8 +82,13 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { */ public static final int PADDING_MODE_STACK = 1; - /** Value used for undefined start and end insets. */ - private static final int UNDEFINED_INSET = Integer.MIN_VALUE; + /** + * Value used for undefined start and end insets. + * + * @see #getLayerInsetStart(int) + * @see #getLayerInsetEnd(int) + */ + public static final int UNDEFINED_INSET = Integer.MIN_VALUE; LayerState mLayerState; @@ -867,7 +872,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { /** * @param index the index of the layer - * @return number of pixels to inset from the start bound + * @return the number of pixels to inset from the start bound, or + * {@link #UNDEFINED_INSET} if not specified * @attr ref android.R.styleable#LayerDrawableItem_start */ public int getLayerInsetStart(int index) { @@ -877,7 +883,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { /** * @param index the index of the layer to adjust - * @param e number of pixels to inset from the end bound + * @param e number of pixels to inset from the end bound, or + * {@link #UNDEFINED_INSET} if not specified * @attr ref android.R.styleable#LayerDrawableItem_end */ public void setLayerInsetEnd(int index, int e) { @@ -977,34 +984,33 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { computeStackedPadding(padding); } + final int paddingT = layerState.mPaddingTop; + final int paddingB = layerState.mPaddingBottom; + + // Resolve padding for RTL. Relative padding overrides absolute + // padding. + final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; + final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart; + final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd; + final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft; + final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight; + // If padding was explicitly specified (e.g. not -1) then override the // computed padding in that dimension. - if (layerState.mPaddingTop >= 0) { - padding.top = layerState.mPaddingTop; - } - - if (layerState.mPaddingBottom >= 0) { - padding.bottom = layerState.mPaddingBottom; + if (paddingL >= 0) { + padding.left = paddingL; } - final int paddingRtlLeft; - final int paddingRtlRight; - if (getLayoutDirection() == LayoutDirection.RTL) { - paddingRtlLeft = layerState.mPaddingEnd; - paddingRtlRight = layerState.mPaddingStart; - } else { - paddingRtlLeft = layerState.mPaddingStart; - paddingRtlRight = layerState.mPaddingEnd; + if (paddingT >= 0) { + padding.top = paddingT; } - final int paddingLeft = paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft; - if (paddingLeft >= 0) { - padding.left = paddingLeft; + if (paddingR >= 0) { + padding.right = paddingR; } - final int paddingRight = paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight; - if (paddingRight >= 0) { - padding.right = paddingRight; + if (paddingB >= 0) { + padding.bottom = paddingB; } return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0; @@ -1471,58 +1477,59 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { } private void updateLayerBounds(Rect bounds) { - int padL = 0; - int padT = 0; - int padR = 0; - int padB = 0; + int paddingL = 0; + int paddingT = 0; + int paddingR = 0; + int paddingB = 0; final Rect outRect = mTmpOutRect; final int layoutDirection = getLayoutDirection(); - final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; + final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL; + final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST; final ChildDrawable[] array = mLayerState.mChildren; - final int N = mLayerState.mNum; - for (int i = 0; i < N; i++) { + + for (int i = 0, count = mLayerState.mNum; i < count; i++) { final ChildDrawable r = array[i]; final Drawable d = r.mDrawable; if (d == null) { continue; } - final Rect container = mTmpContainer; - container.set(d.getBounds()); + final int insetT = r.mInsetT; + final int insetB = r.mInsetB; - // Take the resolved layout direction into account. If start / end - // padding are defined, they will be resolved (hence overriding) to - // left / right or right / left depending on the resolved layout - // direction. If start / end padding are not defined, use the - // left / right ones. - final int insetL, insetR; - if (layoutDirection == LayoutDirection.RTL) { - insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE; - insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS; - } else { - insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS; - insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE; - } + // Resolve insets for RTL. Relative insets override absolute + // insets. + final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; + final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; + final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL; + final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR; // Establish containing region based on aggregate padding and // requested insets for the current layer. - container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT, - bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB); - - // Apply resolved gravity to drawable based on resolved size. - final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight, - d.getIntrinsicWidth(), d.getIntrinsicHeight()); - final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth; - final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight; - Gravity.apply(gravity, w, h, container, outRect, layoutDirection); + final Rect container = mTmpContainer; + container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT, + bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB); + + // Compute a reasonable default gravity based on the intrinsic and + // explicit dimensions, if specified. + final int intrinsicW = d.getIntrinsicWidth(); + final int intrinsicH = d.getIntrinsicHeight(); + final int layerW = r.mWidth; + final int layerH = r.mHeight; + final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH); + + // Explicit dimensions override intrinsic dimensions. + final int resolvedW = layerW < 0 ? intrinsicW : layerW; + final int resolvedH = layerH < 0 ? intrinsicH : layerH; + Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection); d.setBounds(outRect); - if (nest) { - padL += mPaddingL[i]; - padR += mPaddingR[i]; - padT += mPaddingT[i]; - padB += mPaddingB[i]; + if (isPaddingNested) { + paddingL += mPaddingL[i]; + paddingR += mPaddingR[i]; + paddingT += mPaddingT[i]; + paddingB += mPaddingB[i]; } } } @@ -1578,6 +1585,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { int padR = 0; final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST; + final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL; final ChildDrawable[] array = mLayerState.mChildren; final int N = mLayerState.mNum; for (int i = 0; i < N; i++) { @@ -1591,15 +1599,10 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { // left / right or right / left depending on the resolved layout // direction. If start / end padding are not defined, use the // left / right ones. - final int insetL, insetR; - final int layoutDirection = getLayoutDirection(); - if (layoutDirection == LayoutDirection.RTL) { - insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE; - insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS; - } else { - insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS; - insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE; - } + final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS; + final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE; + final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL; + final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR; // Don't apply padding and insets for children that don't have // an intrinsic dimension. @@ -1659,8 +1662,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { if (r.mDrawable != null) { final Rect rect = mTmpRect; r.mDrawable.getPadding(rect); - if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] || - rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) { + if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] + || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) { mPaddingL[i] = rect.left; mPaddingT[i] = rect.top; mPaddingR[i] = rect.right; diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 130cc80a6220..976f77518337 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -162,15 +162,15 @@ void SkiaCanvasProxy::willSave() { mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); } -SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr, - const SkPaint* paint, SaveFlags flags) { +SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) { SkRect rect; - if (rectPtr) { - rect = *rectPtr; - } else if(!mCanvas->getClipBounds(&rect)) { + if (saveLayerRec.fBounds) { + rect = *saveLayerRec.fBounds; + } else if (!mCanvas->getClipBounds(&rect)) { rect = SkRect::MakeEmpty(); } - mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags); + mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint, + (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags)); return SkCanvas::kNoLayer_SaveLayerStrategy; } diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index 0089fb5da2a9..e342d192ea31 100644 --- a/libs/hwui/SkiaCanvasProxy.h +++ b/libs/hwui/SkiaCanvasProxy.h @@ -47,7 +47,7 @@ protected: virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override; virtual void willSave() override; - virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override; + virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; virtual void willRestore() override; virtual void didConcat(const SkMatrix&) override; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index a09240852a38..ea1690fd2691 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.List; /** * AudioManager provides access to volume and ringer mode control. @@ -2158,36 +2159,73 @@ public class AudioManager { } /** - * Handler for audio focus events coming from the audio service. + * Handler for events (audio focus change, recording config change) coming from the + * audio service. */ - private final FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate = - new FocusEventHandlerDelegate(); + private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate = + new ServiceEventHandlerDelegate(); /** - * Helper class to handle the forwarding of audio focus events to the appropriate listener + * Event types */ - private class FocusEventHandlerDelegate { + private final static int MSSG_FOCUS_CHANGE = 0; + private final static int MSSG_RECORDING_CONFIG_CHANGE = 1; + + /** + * Helper class to handle the forwarding of audio service events to the appropriate listener + */ + private class ServiceEventHandlerDelegate { private final Handler mHandler; - FocusEventHandlerDelegate() { + ServiceEventHandlerDelegate() { Looper looper; if ((looper = Looper.myLooper()) == null) { looper = Looper.getMainLooper(); } if (looper != null) { - // implement the event handler delegate to receive audio focus events + // implement the event handler delegate to receive events from audio service mHandler = new Handler(looper) { @Override public void handleMessage(Message msg) { - OnAudioFocusChangeListener listener = null; - synchronized(mFocusListenerLock) { - listener = findFocusListener((String)msg.obj); - } - if (listener != null) { - Log.d(TAG, "AudioManager dispatching onAudioFocusChange(" - + msg.what + ") for " + msg.obj); - listener.onAudioFocusChange(msg.what); + switch (msg.what) { + case MSSG_FOCUS_CHANGE: + OnAudioFocusChangeListener listener = null; + synchronized(mFocusListenerLock) { + listener = findFocusListener((String)msg.obj); + } + if (listener != null) { + Log.d(TAG, "AudioManager dispatching onAudioFocusChange(" + + msg.what + ") for " + msg.obj); + listener.onAudioFocusChange(msg.arg1); + } + break; + case MSSG_RECORDING_CONFIG_CHANGE: + // optimizing for the case of a single callback + AudioRecordingCallback singleCallback = null; + ArrayList<AudioRecordingCallback> multipleCallbacks = null; + synchronized(mRecordCallbackLock) { + if ((mRecordCallbackList != null) + && (mRecordCallbackList.size() != 0)) { + if (mRecordCallbackList.size() == 1) { + singleCallback = mRecordCallbackList.get(0); + } else { + multipleCallbacks = + new ArrayList<AudioRecordingCallback>( + mRecordCallbackList); + } + } + } + if (singleCallback != null) { + singleCallback.onRecordConfigChanged(); + } else if (multipleCallbacks != null) { + for (int i=0 ; i < multipleCallbacks.size() ; i++) { + multipleCallbacks.get(i).onRecordConfigChanged(); + } + } + break; + default: + Log.e(TAG, "Unknown event " + msg.what); } } }; @@ -2204,8 +2242,9 @@ public class AudioManager { private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() { public void dispatchAudioFocusChange(int focusChange, String id) { - Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id); - mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m); + final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage( + MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/); + mServiceEventHandlerDelegate.getHandler().sendMessage(m); } }; @@ -2702,6 +2741,8 @@ public class AudioManager { } + //==================================================================== + // Audio policy /** * @hide * Register the given {@link AudioPolicy}. @@ -2754,6 +2795,131 @@ public class AudioManager { } + //==================================================================== + // Recording configuration + /** + * @hide + * candidate for public API + */ + public static abstract class AudioRecordingCallback { + /** + * @hide + * candidate for public API + */ + public void onRecordConfigChanged() {} + } + + /** + * @hide + * candidate for public API + * @param non-null callback + */ + public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb) { + if (cb == null) { + throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument"); + } + synchronized(mRecordCallbackLock) { + // lazy initialization of the list of recording callbacks + if (mRecordCallbackList == null) { + mRecordCallbackList = new ArrayList<AudioRecordingCallback>(); + } + final int oldCbCount = mRecordCallbackList.size(); + if (!mRecordCallbackList.contains(cb)) { + mRecordCallbackList.add(cb); + final int newCbCount = mRecordCallbackList.size(); + if ((oldCbCount == 0) && (newCbCount > 0)) { + // register binder for callbacks + final IAudioService service = getService(); + try { + service.registerRecordingCallback(mRecCb); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in registerRecordingCallback", e); + } + } + } else { + Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously" + + "registered callback"); + } + } + } + + /** + * @hide + * candidate for public API + * @param non-null callback + */ + public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) { + if (cb == null) { + throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument"); + } + synchronized(mRecordCallbackLock) { + if (mRecordCallbackList == null) { + return; + } + final int oldCbCount = mRecordCallbackList.size(); + if (mRecordCallbackList.remove(cb)) { + final int newCbCount = mRecordCallbackList.size(); + if ((oldCbCount > 0) && (newCbCount == 0)) { + // unregister binder for callbacks + final IAudioService service = getService(); + try { + service.unregisterRecordingCallback(mRecCb); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in unregisterRecordingCallback", e); + } + } + } else { + Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback" + + " already unregistered or never registered"); + } + } + } + + /** + * @hide + * candidate for public API + * @return a non-null array of recording configurations. An array of length 0 indicates there is + * no recording active when queried. + */ + public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() { + final IAudioService service = getService(); + try { + return service.getActiveRecordConfigurations(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to retrieve active record configurations", e); + return null; + } + } + + /** + * constants for the recording events, to keep in sync + * with frameworks/av/include/media/AudioPolicy.h + */ + /** @hide */ + public final static int RECORD_CONFIG_EVENT_START = 1; + /** @hide */ + public final static int RECORD_CONFIG_EVENT_STOP = 0; + + /** + * All operations on this list are sync'd on mRecordCallbackLock. + * List is lazy-initialized in {@link #registerAudioRecordingCallback(AudioRecordingCallback)}. + * List can be null. + */ + private List<AudioRecordingCallback> mRecordCallbackList; + private final Object mRecordCallbackLock = new Object(); + + private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() { + + public void dispatchRecordingConfigChange() { + final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage( + MSSG_RECORDING_CONFIG_CHANGE/*what*/); + mServiceEventHandlerDelegate.getHandler().sendMessage(m); + } + + }; + + //===================================================================== + /** * @hide * Reload audio settings. This method is called by Settings backup diff --git a/media/java/android/media/AudioRecordConfiguration.aidl b/media/java/android/media/AudioRecordConfiguration.aidl new file mode 100644 index 000000000000..afe912b10b8f --- /dev/null +++ b/media/java/android/media/AudioRecordConfiguration.aidl @@ -0,0 +1,18 @@ +/* Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.media; + +parcelable AudioRecordConfiguration; diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java new file mode 100644 index 000000000000..aefe692de651 --- /dev/null +++ b/media/java/android/media/AudioRecordConfiguration.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * @hide + * Candidate for public API, see AudioManager.getActiveRecordConfiguration() + * + */ +public class AudioRecordConfiguration implements Parcelable { + + private final int mSessionId; + + private final int mClientSource; + + /** + * @hide + */ + public AudioRecordConfiguration(int session, int source) { + mSessionId = session; + mClientSource = source; + } + + /** + * @return one of AudioSource.MIC, AudioSource.VOICE_UPLINK, + * AudioSource.VOICE_DOWNLINK, AudioSource.VOICE_CALL, + * AudioSource.CAMCORDER, AudioSource.VOICE_RECOGNITION, + * AudioSource.VOICE_COMMUNICATION. + */ + public int getClientAudioSource() { return mClientSource; } + + /** + * @return the session number of the recorder. + */ + public int getAudioSessionId() { return mSessionId; } + + + public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR + = new Parcelable.Creator<AudioRecordConfiguration>() { + /** + * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel(). + * @param p Parcel object to read the AudioRecordConfiguration from + * @return a new AudioRecordConfiguration created from the data in the parcel + */ + public AudioRecordConfiguration createFromParcel(Parcel p) { + return new AudioRecordConfiguration(p); + } + public AudioRecordConfiguration[] newArray(int size) { + return new AudioRecordConfiguration[size]; + } + }; + + @Override + public int hashCode() { + return Objects.hash(mSessionId, mClientSource); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSessionId); + dest.writeInt(mClientSource); + } + + private AudioRecordConfiguration(Parcel in) { + mSessionId = in.readInt(); + mClientSource = in.readInt(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof AudioRecordConfiguration)) return false; + + final AudioRecordConfiguration that = (AudioRecordConfiguration) o; + return ((mSessionId == that.mSessionId) + && (mClientSource == that.mClientSource)); + } +}
\ No newline at end of file diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 7bfd7ca4d56d..aa0d78dacd7e 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -227,7 +227,7 @@ public class AudioSystem } /** - * Handles events for the audio policy manager about dynamic audio policies + * Handles events from the audio policy manager about dynamic audio policies * @see android.media.audiopolicy.AudioPolicy */ public interface DynamicPolicyCallback @@ -267,6 +267,33 @@ public class AudioSystem } } + /** + * Handles events from the audio policy manager about recording events + * @see android.media.AudioManager.AudioRecordingCallback + */ + public interface AudioRecordingCallback + { + void onRecordingConfigurationChanged(int event, int session, int source); + } + + private static AudioRecordingCallback sRecordingCallback; + + public static void setRecordingCallback(AudioRecordingCallback cb) { + synchronized (AudioSystem.class) { + sRecordingCallback = cb; + native_register_recording_callback(); + } + } + + private static void recordingCallbackFromNative(int event, int session, int source) { + AudioRecordingCallback cb = null; + synchronized (AudioSystem.class) { + cb = sRecordingCallback; + } + if (cb != null) { + cb.onRecordingConfigurationChanged(event, session, source); + } + } /* * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...) @@ -646,6 +673,8 @@ public class AudioSystem // declare this instance as having a dynamic policy callback handler private static native final void native_register_dynamic_policy_callback(); + // declare this instance as having a recording configuration update callback handler + private static native final void native_register_recording_callback(); // must be kept in sync with value in include/system/audio.h public static final int AUDIO_HW_SYNC_INVALID = 0; diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dbb7661046c3..abe92c727cc5 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -20,9 +20,11 @@ import android.app.PendingIntent; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.media.AudioAttributes; +import android.media.AudioRecordConfiguration; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; +import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.IVolumeController; import android.media.Rating; @@ -161,4 +163,10 @@ interface IAudioService { int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb); void setVolumePolicy(in VolumePolicy policy); + + void registerRecordingCallback(in IRecordingConfigDispatcher rcdb); + + oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb); + + AudioRecordConfiguration[] getActiveRecordConfigurations(); } diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl new file mode 100644 index 000000000000..a5eb8b9fa787 --- /dev/null +++ b/media/java/android/media/IRecordingConfigDispatcher.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +/** + * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates. + * + * {@hide} + */ +oneway interface IRecordingConfigDispatcher { + + void dispatchRecordingConfigChange(); + +} diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 478fd992c3bd..f1f84375c36b 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2175,8 +2175,6 @@ final public class MediaCodec { int offset, int size, long presentationTimeUs, int flags) throws CryptoException; - // The following mode constants MUST stay in sync with their equivalents - // in media/hardware/CryptoAPI.h ! public static final int CRYPTO_MODE_UNENCRYPTED = 0; public static final int CRYPTO_MODE_AES_CTR = 1; public static final int CRYPTO_MODE_AES_CBC = 2; diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 9bcb5e35938b..0fba9925b6c1 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1735,8 +1735,7 @@ public final class MediaCodecInfo { CodecProfileLevel[] profileLevels = mParent.profileLevels; String mime = mParent.getMimeType(); - if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC) || - mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_AVC)) { + if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) { maxBlocks = 99; maxBlocksPerSecond = 1485; maxBps = 64000; @@ -2090,8 +2089,7 @@ public final class MediaCodecInfo { applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE, maxBlocks, maxBlocksPerSecond, blockSize, blockSize, 1 /* widthAlignment */, 1 /* heightAlignment */); - } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) || - mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_HEVC)) { + } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) { maxBlocks = 36864; maxBlocksPerSecond = maxBlocks * 15; maxBps = 128000; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index a102e51de3d0..b2fa0acf7f57 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -92,8 +92,6 @@ public final class MediaFormat { public static final String MIMETYPE_VIDEO_H263 = "video/3gpp"; public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; public static final String MIMETYPE_VIDEO_RAW = "video/raw"; - public static final String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc"; - public static final String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc"; public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java index 6ec16dba42ac..dc89a56cf0b3 100644 --- a/media/java/android/mtp/MtpEvent.java +++ b/media/java/android/mtp/MtpEvent.java @@ -18,15 +18,152 @@ package android.mtp; /** * This class encapsulates information about a MTP event. - * Event constants are defined by the USB-IF MTP specification. + * This corresponds to the events described in appendix G of the MTP specification. */ public class MtpEvent { private int mEventCode = MtpConstants.EVENT_UNDEFINED; + // Parameters for event. The interpretation of event parameters depends upon mEventCode. + private int mParameter1; + private int mParameter2; + private int mParameter3; + /** * Returns event code of MTP event. * See the USB-IF MTP specification for the details of event constants. * @return event code */ public int getEventCode() { return mEventCode; } + + /** + * Obtains the first event parameter. + */ + public int getParameter1() { return mParameter1; } + + /** + * Obtains the second event parameter. + */ + public int getParameter2() { return mParameter2; } + + /** + * Obtains the third event parameter. + */ + public int getParameter3() { return mParameter3; } + + /** + * Obtains objectHandle event parameter. + * + * @see MtpConstants#EVENT_OBJECT_ADDED + * @see MtpConstants#EVENT_OBJECT_REMOVED + * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED + * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER + * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED + * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED + */ + public int getObjectHandle() { + switch (mEventCode) { + case MtpConstants.EVENT_OBJECT_ADDED: + return mParameter1; + case MtpConstants.EVENT_OBJECT_REMOVED: + return mParameter1; + case MtpConstants.EVENT_OBJECT_INFO_CHANGED: + return mParameter1; + case MtpConstants.EVENT_REQUEST_OBJECT_TRANSFER: + return mParameter1; + case MtpConstants.EVENT_OBJECT_PROP_CHANGED: + return mParameter1; + case MtpConstants.EVENT_OBJECT_REFERENCES_CHANGED: + return mParameter1; + default: + throw new IllegalParameterAccess("objectHandle", mEventCode); + } + } + + /** + * Obtains storageID event parameter. + * + * @see MtpConstants#EVENT_STORE_ADDED + * @see MtpConstants#EVENT_STORE_REMOVED + * @see MtpConstants#EVENT_STORE_FULL + * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED + */ + public int getStorageId() { + switch (mEventCode) { + case MtpConstants.EVENT_STORE_ADDED: + return mParameter1; + case MtpConstants.EVENT_STORE_REMOVED: + return mParameter1; + case MtpConstants.EVENT_STORE_FULL: + return mParameter1; + case MtpConstants.EVENT_STORAGE_INFO_CHANGED: + return mParameter1; + default: + throw new IllegalParameterAccess("storageID", mEventCode); + } + } + + /** + * Obtains devicePropCode event parameter. + * + * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED + */ + public int getDevicePropCode() { + switch (mEventCode) { + case MtpConstants.EVENT_DEVICE_PROP_CHANGED: + return mParameter1; + default: + throw new IllegalParameterAccess("devicePropCode", mEventCode); + } + } + + /** + * Obtains transactionID event parameter. + * + * @see MtpConstants#EVENT_CAPTURE_COMPLETE + */ + public int getTransactionId() { + switch (mEventCode) { + case MtpConstants.EVENT_CAPTURE_COMPLETE: + return mParameter1; + default: + throw new IllegalParameterAccess("transactionID", mEventCode); + } + } + + /** + * Obtains objectPropCode event parameter. + * + * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED + * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED + */ + public int getObjectPropCode() { + switch (mEventCode) { + case MtpConstants.EVENT_OBJECT_PROP_CHANGED: + return mParameter2; + case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED: + return mParameter1; + default: + throw new IllegalParameterAccess("objectPropCode", mEventCode); + } + } + + /** + * Obtains objectFormatCode event parameter. + * + * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED + */ + public int getObjectFormatCode() { + switch (mEventCode) { + case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED: + return mParameter2; + default: + throw new IllegalParameterAccess("objectFormatCode", mEventCode); + } + } + + private static class IllegalParameterAccess extends UnsupportedOperationException { + public IllegalParameterAccess(String propertyName, int eventCode) { + super("Cannot obtain " + propertyName + " for the event: " + eventCode + "."); + } + } } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 49b579ce438d..2004a3aef2b8 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -85,6 +85,13 @@ static struct { jmethodID setNativeObjectLocked; } gPersistentSurfaceClassInfo; +static struct { + jint Unencrypted; + jint AesCtr; + jint AesCbc; +} gCryptoModes; + + struct fields_t { jfieldID context; jmethodID postEventFromNativeID; @@ -94,6 +101,9 @@ struct fields_t { jfieldID cryptoInfoKeyID; jfieldID cryptoInfoIVID; jfieldID cryptoInfoModeID; + jfieldID cryptoInfoPatternID; + jfieldID patternEncryptBlocksID; + jfieldID patternSkipBlocksID; }; static fields_t gFields; @@ -325,11 +335,12 @@ status_t JMediaCodec::queueSecureInputBuffer( const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern &pattern, int64_t presentationTimeUs, uint32_t flags, AString *errorDetailMsg) { return mCodec->queueSecureInputBuffer( - index, offset, subSamples, numSubSamples, key, iv, mode, + index, offset, subSamples, numSubSamples, key, iv, mode, pattern, presentationTimeUs, flags, errorDetailMsg); } @@ -1275,7 +1286,26 @@ static void android_media_MediaCodec_queueSecureInputBuffer( jbyteArray ivObj = (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID); - jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); + jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); + enum CryptoPlugin::Mode mode; + if (jmode == gCryptoModes.Unencrypted) { + mode = CryptoPlugin::kMode_Unencrypted; + } else if (jmode == gCryptoModes.AesCtr) { + mode = CryptoPlugin::kMode_AES_CTR; + } else if (jmode == gCryptoModes.AesCbc) { + mode = CryptoPlugin::kMode_AES_CBC; + } else { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } + + jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID); + + CryptoPlugin::Pattern pattern; + if (patternObj != NULL) { + pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID); + pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID); + } status_t err = OK; @@ -1360,7 +1390,8 @@ static void android_media_MediaCodec_queueSecureInputBuffer( index, offset, subSamples, numSubSamples, (const uint8_t *)key, (const uint8_t *)iv, - (CryptoPlugin::Mode)mode, + mode, + pattern, timestampUs, flags, &errorDetailMsg); @@ -1658,6 +1689,22 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { CHECK(gFields.postEventFromNativeID != NULL); + jfieldID field; + field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I"); + CHECK(field != NULL); + gCryptoModes.Unencrypted = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CTR", "I"); + CHECK(field != NULL); + gCryptoModes.AesCtr = + env->GetStaticIntField(clazz.get(), field); + + field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CBC", "I"); + CHECK(field != NULL); + gCryptoModes.AesCbc = + env->GetStaticIntField(clazz.get(), field); + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo")); CHECK(clazz.get() != NULL); @@ -1682,10 +1729,22 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I"); CHECK(gFields.cryptoInfoModeID != NULL); + gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern", + "Landroid/media/MediaCodec$CryptoInfo$Pattern;"); + CHECK(gFields.cryptoInfoPatternID != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo$Pattern")); + CHECK(clazz.get() != NULL); + + gFields.patternEncryptBlocksID = env->GetFieldID(clazz.get(), "mEncryptBlocks", "I"); + CHECK(gFields.patternEncryptBlocksID != NULL); + + gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I"); + CHECK(gFields.patternSkipBlocksID != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException")); CHECK(clazz.get() != NULL); - jfieldID field; field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I"); CHECK(field != NULL); gCryptoErrorCodes.cryptoErrorNoKey = diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index 6650cf9254e1..c0c47ef2aeda 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -81,6 +81,7 @@ struct JMediaCodec : public AHandler { const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern &pattern, int64_t presentationTimeUs, uint32_t flags, AString *errorDetailMsg); diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 4aa12c2d6ffc..130dfe554f89 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -98,6 +98,9 @@ static jfieldID field_objectInfo_keywords; // MtpEvent fields static jfieldID field_event_eventCode; +static jfieldID field_event_parameter1; +static jfieldID field_event_parameter2; +static jfieldID field_event_parameter3; class JavaArrayWriter { public: @@ -573,13 +576,17 @@ static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thi env->ThrowNew(clazz_io_exception, ""); return NULL; } - const int eventCode = device->reapEventRequest(seq); + uint32_t parameters[3]; + const int eventCode = device->reapEventRequest(seq, ¶meters); if (eventCode <= 0) { env->ThrowNew(clazz_operation_canceled_exception, ""); return NULL; } jobject result = env->NewObject(clazz_event, constructor_event); env->SetIntField(result, field_event_eventCode, eventCode); + env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0])); + env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1])); + env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2])); return result; } @@ -832,6 +839,21 @@ int register_android_mtp_MtpDevice(JNIEnv *env) ALOGE("Can't find MtpObjectInfo.mEventCode"); return -1; } + field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I"); + if (field_event_parameter1 == NULL) { + ALOGE("Can't find MtpObjectInfo.mParameter1"); + return -1; + } + field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I"); + if (field_event_parameter2 == NULL) { + ALOGE("Can't find MtpObjectInfo.mParameter2"); + return -1; + } + field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I"); + if (field_event_parameter3 == NULL) { + ALOGE("Can't find MtpObjectInfo.mParameter3"); + return -1; + } clazz_event = (jclass)env->NewGlobalRef(clazz); clazz = env->FindClass("android/mtp/MtpDevice"); diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index abb464eb3a67..6fb8b518acf5 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -56,7 +56,6 @@ import java.util.Random; public class CaptivePortalLoginActivity extends Activity { private static final String TAG = "CaptivePortalLogin"; - private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com"; private static final int SOCKET_TIMEOUT_MS = 10000; private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS }; @@ -72,16 +71,14 @@ public class CaptivePortalLoginActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - String server = Settings.Global.getString(getContentResolver(), "captive_portal_server"); - if (server == null) server = DEFAULT_SERVER; mCm = ConnectivityManager.from(this); String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); + if (url == null) url = mCm.getCaptivePortalServerUrl(); try { - mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204"); + mURL = new URL(url); } catch (MalformedURLException e) { // System misconfigured, bail out in a way that at least provides network access. - Log.e(TAG, "Invalid captive portal URL, server=" + server); + Log.e(TAG, "Invalid captive portal URL, url=" + url); done(Result.WANTED_AS_IS); } mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml index f9bbccb57809..1b5911dba931 100644 --- a/packages/DocumentsUI/res/layout/fragment_directory.xml +++ b/packages/DocumentsUI/res/layout/fragment_directory.xml @@ -23,11 +23,11 @@ <ProgressBar android:id="@+id/progressbar" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/progress_bar_height" android:indeterminate="true" style="@style/TrimmedHorizontalProgressBar" android:visibility="gone"/> - + <FrameLayout android:id="@+id/container_message_bar" android:layout_width="match_parent" @@ -44,7 +44,7 @@ android:layout_height="match_parent" android:orientation="vertical" android:visibility="gone"> - + <TextView android:id="@+id/message" android:layout_width="wrap_content" @@ -58,9 +58,9 @@ android:layout_height="wrap_content" android:text="@string/button_retry" style="?android:attr/buttonBarPositiveButtonStyle" /> - + </LinearLayout> - + <!-- This FrameLayout works around b/24189541 --> <FrameLayout android:layout_width="match_parent" @@ -68,6 +68,7 @@ <android.support.v7.widget.RecyclerView android:id="@+id/list" + android:background="@color/window_background" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml index cacdf4d2f99d..5adb165667c4 100644 --- a/packages/DocumentsUI/res/values/dimens.xml +++ b/packages/DocumentsUI/res/values/dimens.xml @@ -15,6 +15,9 @@ --> <resources> + <dimen name="grid_container_padding">10dp</dimen> + <dimen name="list_container_padding">0dp</dimen> + <dimen name="icon_size">40dp</dimen> <dimen name="root_icon_size">24dp</dimen> <dimen name="root_icon_margin">0dp</dimen> @@ -23,6 +26,8 @@ <dimen name="list_item_thumbnail_size">40dp</dimen> <dimen name="grid_item_icon_size">30dp</dimen> + <dimen name="progress_bar_height">4dp</dimen> + <dimen name="grid_width">152dp</dimen> <dimen name="grid_height">176dp</dimen> diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 898713f75e62..22e81c6c07ad 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -601,11 +601,11 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi throw new IllegalArgumentException("Unsupported layout mode: " + mode); } - mRecView.setLayoutManager(layout); - // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of - // imperatively calling this function. - mSelectionManager.handleLayoutChanged(); + int pad = getDirectoryPadding(mode); + mRecView.setPadding(pad, pad, pad, pad); // setting layout manager automatically invalidates existing ViewHolders. + mRecView.setLayoutManager(layout); + mSelectionManager.handleLayoutChanged(); // RecyclerView doesn't do this for us mIconHelper.setMode(mode); } @@ -621,6 +621,20 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi return columnCount; } + private int getDirectoryPadding(int mode) { + switch (mode) { + case MODE_GRID: + return getResources().getDimensionPixelSize( + R.dimen.grid_container_padding); + case MODE_LIST: + return getResources().getDimensionPixelSize( + R.dimen.list_container_padding); + case MODE_UNKNOWN: + default: + throw new IllegalArgumentException("Unsupported layout mode: " + mode); + } + } + @Override public int getColumnCount() { return mColumnCount; diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java deleted file mode 100644 index d1f8ff787833..000000000000 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.documentsui.dirlist; - -import android.content.Context; -import android.database.Cursor; -import android.view.View; -import android.widget.Space; - -import com.android.documentsui.R; -import com.android.documentsui.State; - -final class EmptyDocumentHolder extends DocumentHolder { - final int mVisibleHeight; - - public EmptyDocumentHolder(Context context) { - super(context, new Space(context)); - - // Per UX spec, this puts a bigger gap between the folders and documents in the grid. - mVisibleHeight = context.getResources().getDimensionPixelSize(R.dimen.grid_item_margin) * 2; - } - - public void bind(State state) { - bind(null, null, state); - } - - @Override - public void bind(Cursor cursor, String modelId, State state) { - if (state.derivedMode == State.MODE_GRID) { - itemView.setMinimumHeight(mVisibleHeight); - } else { - itemView.setMinimumHeight(0); - } - return; - } -} diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java index 3ee1d4229bbe..2485ad96a233 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java @@ -18,10 +18,16 @@ package com.android.documentsui.dirlist; import static com.android.internal.util.Preconditions.checkArgument; +import android.content.Context; +import android.database.Cursor; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView.AdapterDataObserver; import android.util.SparseArray; import android.view.ViewGroup; +import android.widget.Space; + +import com.android.documentsui.R; +import com.android.documentsui.State; import java.util.List; @@ -222,4 +228,33 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter { throw new UnsupportedOperationException(); } } + + /** + * The most elegant transparent blank box that spans N rows ever conceived. + */ + private static final class EmptyDocumentHolder extends DocumentHolder { + final int mVisibleHeight; + + public EmptyDocumentHolder(Context context) { + super(context, new Space(context)); + + // Per UX spec, this puts a bigger gap between the folders and documents in the grid. + mVisibleHeight = context.getResources().getDimensionPixelSize( + R.dimen.grid_item_margin); + } + + public void bind(State state) { + bind(null, null, state); + } + + @Override + public void bind(Cursor cursor, String modelId, State state) { + if (state.derivedMode == State.MODE_GRID) { + itemView.setMinimumHeight(mVisibleHeight); + } else { + itemView.setMinimumHeight(0); + } + return; + } + } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java index c216c7760096..15b8ef3cae9a 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java @@ -7,9 +7,6 @@ import android.os.Process; import android.provider.DocumentsContract; import android.util.Log; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java index 49b48c5bb66d..7527f549e87f 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java @@ -19,6 +19,8 @@ package com.android.mtp; import android.content.Context; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import android.mtp.MtpConstants; +import android.mtp.MtpEvent; import android.os.CancellationSignal; import android.os.OperationCanceledException; import android.os.SystemClock; @@ -32,12 +34,10 @@ import java.util.concurrent.TimeUnit; @RealDeviceTest public class MtpManagerTest extends InstrumentationTestCase { - private static final int TIMEOUT_MS = 1000; UsbManager mUsbManager; MtpManager mManager; UsbDevice mUsbDevice; - int mRequest; @Override public void setUp() throws Exception { @@ -85,6 +85,19 @@ public class MtpManagerTest extends InstrumentationTestCase { getInstrumentation().show(Arrays.toString(records[0].operationsSupported)); } + public void testEventObjectAdded() throws Exception { + while (true) { + getInstrumentation().show("Please take a photo by using connected MTP device."); + final CancellationSignal signal = new CancellationSignal(); + MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal); + if (event.getEventCode() != MtpConstants.EVENT_OBJECT_ADDED) { + continue; + } + assertTrue(event.getObjectHandle() != 0); + break; + } + } + private Context getContext() { return getInstrumentation().getContext(); } diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk index c8606686749d..2189b55357af 100644 --- a/packages/SettingsLib/Android.mk +++ b/packages/SettingsLib/Android.mk @@ -3,9 +3,22 @@ include $(CLEAR_VARS) LOCAL_MODULE := SettingsLib -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-recyclerview \ + android-support-v7-preference \ + android-support-v7-appcompat \ + android-support-v14-preference + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ + frameworks/support/v7/preference/res \ + frameworks/support/v14/preference/res \ + frameworks/support/v7/appcompat/res \ + frameworks/support/v7/recyclerview/res -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_AAPT_FLAGS := --auto-add-overlay \ + --extra-packages android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview + include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml new file mode 100644 index 000000000000..b3d7cf925ef2 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="21dp" + android:height="21dp" + android:viewportWidth="21.0" + android:viewportHeight="21.0" + android:tint="?android:attr/colorAccent"> + <path + android:fillColor="@android:color/white" + android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" /> +</vector> diff --git a/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml new file mode 100644 index 000000000000..f7a9c9f1816b --- /dev/null +++ b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + style="?android:attr/spinnerDropDownItemStyle" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="?android:attr/listPreferredItemHeightSmall" + android:ellipsize="marquee" />
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml new file mode 100644 index 000000000000..46267a218a3f --- /dev/null +++ b/packages/SettingsLib/res/values/attrs.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <declare-styleable name="RestrictedPreference"> + <attr name="userRestriction" format="string"/> + </declare-styleable> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml new file mode 100644 index 000000000000..c090468c3e8f --- /dev/null +++ b/packages/SettingsLib/res/values/colors.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <color name="disabled_text_color">#66000000</color> <!-- 38% black --> +</resources> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index d7c78f6ffa6e..9a1d6a404b08 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -31,4 +31,8 @@ <dimen name="user_spinner_padding">4dp</dimen> <dimen name="user_spinner_padding_sides">20dp</dimen> <dimen name="user_spinner_item_height">56dp</dimen> + + <!-- Lock icon for preferences locked by admin --> + <dimen name="restricted_lock_icon_size">16dp</dimen> + <dimen name="restricted_lock_icon_padding">4dp</dimen> </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index ac19cf50d62a..6dfa9ad2d26e 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -684,6 +684,8 @@ <string name="select_webview_provider_title">WebView implementation</string> <!-- Developer settings: select WebView provider dialog title --> <string name="select_webview_provider_dialog_title">Set WebView implementation</string> + <!-- Developer settings: confirmation dialog text for the WebView provider selection dialog --> + <string name="select_webview_provider_confirmation_text">The chosen WebView implementation is disabled, and must be enabled to be used, do you wish to enable it?</string> <!-- Developer settings screen, convert userdata to file encryption option name --> <string name="convert_to_file_encryption">Convert to file encryption</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java new file mode 100644 index 000000000000..c2f885dabe60 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v7.preference.DropDownPreference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +public class RestrictedDropDownPreference extends DropDownPreference { + private Spinner mSpinner; + private final Drawable mRestrictedPadlock; + private final int mRestrictedPadlockPadding; + private List<RestrictedItem> mRestrictedItems = new ArrayList<>(); + + public RestrictedDropDownPreference(Context context) { + this(context, null); + } + + public RestrictedDropDownPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context); + mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + } + + private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View v, int position, long id) { + if (position >= 0) { + String value = getEntryValues()[position].toString(); + RestrictedItem item = getRestrictedItemForEntryValue(value); + if (item != null) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), + item.enforcedAdmin); + mSpinner.setSelection(findIndexOfValue(getValue())); + } else if (!value.equals(getValue()) && callChangeListener(value)) { + setValue(value); + } + } + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // noop + } + }; + + @Override + protected ArrayAdapter createAdapter() { + return new RestrictedArrayItemAdapter(getContext()); + } + + @Override + public void setValue(String value) { + if (getRestrictedItemForEntryValue(value) != null) { + return; + } + super.setValue(value); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner); + mSpinner.setOnItemSelectedListener(mItemSelectedListener); + } + + private class RestrictedArrayItemAdapter extends ArrayAdapter<String> { + public RestrictedArrayItemAdapter(Context context) { + super(context, R.layout.spinner_dropdown_restricted_item); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + TextView view = (TextView) super.getView(position, convertView, parent); + CharSequence entry = getItem(position); + boolean isEntryRestricted = isRestrictedForEntry(entry); + RestrictedLockUtils.setTextViewPadlock(getContext(), view, isEntryRestricted); + view.setEnabled(!isEntryRestricted); + return view; + } + } + + private boolean isRestrictedForEntry(CharSequence entry) { + if (entry == null) { + return false; + } + for (RestrictedItem item : mRestrictedItems) { + if (entry.equals(item.entry)) { + return true; + } + } + return false; + } + + private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) { + if (entryValue == null) { + return null; + } + for (RestrictedItem item : mRestrictedItems) { + if (entryValue.equals(item.entryValue)) { + return item; + } + } + return null; + } + + public void addRestrictedItem(RestrictedItem item) { + mRestrictedItems.add(item); + } + + public static class RestrictedItem { + public CharSequence entry; + public CharSequence entryValue; + public EnforcedAdmin enforcedAdmin; + + public RestrictedItem(CharSequence entry, CharSequence entryValue, + EnforcedAdmin enforcedAdmin) { + this.entry = entry; + this.entryValue = entryValue; + this.enforcedAdmin = enforcedAdmin; + } + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java new file mode 100644 index 000000000000..e63130de6f4e --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.style.ImageSpan; + +/** + * An extension of ImageSpan which adds a padding before the image. + */ +public class RestrictedLockImageSpan extends ImageSpan { + private Context mContext; + private final float mExtraPadding; + private final Drawable mRestrictedPadlock; + + public RestrictedLockImageSpan(Context context) { + // we are overriding getDrawable, so passing null to super class here. + super((Drawable) null); + + mContext = context; + mExtraPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); + } + + @Override + public Drawable getDrawable() { + return mRestrictedPadlock; + } + + @Override + public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, + int bottom, Paint paint) { + Drawable drawable = getDrawable(); + canvas.save(); + + // Add extra padding before the padlock. + float transX = x + mExtraPadding; + float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent; + + canvas.translate(transX, transY); + drawable.draw(canvas); + canvas.restore(); + } + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, + Paint.FontMetricsInt fontMetrics) { + int size = super.getSize(paint, text, start, end, fontMetrics); + size += 2 * mExtraPadding; + return size; + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java new file mode 100644 index 000000000000..f6caaa9642c4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.Spanned; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; +import android.text.style.ImageSpan; +import android.view.MenuItem; +import android.widget.TextView; + +import java.util.List; + +/** + * Utility class to host methods usable in adding a restricted padlock icon and showing admin + * support message dialog. + */ +public class RestrictedLockUtils { + /** + * @return drawables for displaying with settings that are locked by a device admin. + */ + public static Drawable getRestrictedPadlock(Context context) { + Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline); + final int iconSize = context.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_size); + restrictedPadlock.setBounds(0, 0, iconSize, iconSize); + return restrictedPadlock; + } + + /** + * Checks if a restriction is enforced on a user and returns the enforced admin and + * admin userId. + * + * @param userRestriction Restriction to check + * @param userId User which we need to check if restriction is enforced on. + * @return EnforcedAdmin Object containing the enforce admin and admin user details, or + * {@code null} If the restriction is not set. If the restriction is set by both device owner + * and profile owner, then the admin will be set to {@code null} and userId to + * {@link UserHandle#USER_NULL}. + */ + public static EnforcedAdmin checkIfRestrictionEnforced(Context context, + String userRestriction, int userId) { + DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser(); + int deviceOwnerUserId = dpm.getDeviceOwnerUserId(); + boolean enforcedByDeviceOwner = false; + if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) { + Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId); + if (enforcedRestrictions != null + && enforcedRestrictions.getBoolean(userRestriction, false)) { + enforcedByDeviceOwner = true; + } + } + + ComponentName profileOwner = null; + boolean enforcedByProfileOwner = false; + if (userId != UserHandle.USER_NULL) { + profileOwner = dpm.getProfileOwnerAsUser(userId); + if (profileOwner != null) { + Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId); + if (enforcedRestrictions != null + && enforcedRestrictions.getBoolean(userRestriction, false)) { + enforcedByProfileOwner = true; + } + } + } + + if (!enforcedByDeviceOwner && !enforcedByProfileOwner) { + return null; + } + + EnforcedAdmin admin = null; + if (enforcedByDeviceOwner && enforcedByProfileOwner) { + admin = new EnforcedAdmin(); + } else if (enforcedByDeviceOwner) { + admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId); + } else { + admin = new EnforcedAdmin(profileOwner, userId); + } + return admin; + } + + /** + * Checks if lock screen notification features are disabled by policy. This should be + * only used for keyguard notification features but not the keyguard features + * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user + * as it won't work for that case. + * + * @param keyguardNotificationFeatures Could be any of notification features that can be + * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}. + * @return EnforcedAdmin Object containing the enforce admin and admin user details, or + * {@code null} If the notification features are not disabled. If the restriction is set by + * multiple admins, then the admin will be set to {@code null} and userId to + * {@link UserHandle#USER_NULL}. + */ + public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context, + int keyguardNotificationFeatures) { + final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + boolean isDisabledByMultipleAdmins = false; + ComponentName adminComponent = null; + List<ComponentName> admins = dpm.getActiveAdmins(); + int disabledKeyguardFeatures; + for (ComponentName admin : admins) { + disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin); + if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) { + if (adminComponent == null) { + adminComponent = admin; + } else { + isDisabledByMultipleAdmins = true; + break; + } + } + } + EnforcedAdmin enforcedAdmin = null; + if (adminComponent != null) { + if (!isDisabledByMultipleAdmins) { + enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId()); + } else { + enforcedAdmin = new EnforcedAdmin(); + } + } + return enforcedAdmin; + } + + /** + * Set the menu item as disabled by admin by adding a restricted padlock at the end of the + * text and set the click listener which will send an intent to show the admin support details + * dialog. + */ + public static void setMenuItemAsDisabledByAdmin(final Context context, + final MenuItem item, final EnforcedAdmin admin) { + SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle()); + removeExistingRestrictedSpans(sb); + + final int disabledColor = context.getColor(R.color.disabled_text_color); + sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ImageSpan image = new RestrictedLockImageSpan(context); + sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + item.setTitle(sb); + + item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + sendShowAdminSupportDetailsIntent(context, admin); + return true; + } + }); + } + + private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) { + final int length = sb.length(); + RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length, + RestrictedLockImageSpan.class); + for (ImageSpan span : imageSpans) { + sb.removeSpan(span); + } + ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class); + for (ForegroundColorSpan span : colorSpans) { + sb.removeSpan(span); + } + } + + /** + * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}. + */ + public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { + Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + int adminUserId = UserHandle.myUserId(); + if (admin != null) { + if (admin.component != null) { + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component); + } + if (admin.userId != UserHandle.USER_NULL) { + adminUserId = admin.userId; + } + intent.putExtra(Intent.EXTRA_USER_ID, adminUserId); + } + context.startActivityAsUser(intent, new UserHandle(adminUserId)); + } + + public static void setTextViewPadlock(Context context, + TextView textView, boolean showPadlock) { + final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); + removeExistingRestrictedSpans(sb); + if (showPadlock) { + final ImageSpan image = new RestrictedLockImageSpan(context); + sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + textView.setText(sb); + } + + public static class EnforcedAdmin { + public ComponentName component = null; + public int userId = UserHandle.USER_NULL; + + public EnforcedAdmin(ComponentName component, int userId) { + this.component = component; + this.userId = userId; + } + + public EnforcedAdmin() {} + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java new file mode 100644 index 000000000000..569017a6af36 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.os.UserHandle; +import android.support.v4.content.res.TypedArrayUtils; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Preference class that supports being disabled by a user restriction + * set by a device admin. + */ +public class RestrictedPreference extends Preference { + RestrictedPreferenceHelper mHelper; + + public RestrictedPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public RestrictedPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle, + android.R.attr.preferenceStyle)); + } + + public RestrictedPreference(Context context) { + this(context, null); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mHelper.onBindViewHolder(holder); + } + + @Override + public void performClick() { + if (!mHelper.performClick()) { + super.performClick(); + } + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + mHelper.onAttachedToHierarchy(); + super.onAttachedToHierarchy(preferenceManager); + } + + public void checkRestrictionAndSetDisabled(String userRestriction) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); + } + + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); + } + + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java new file mode 100644 index 000000000000..f04150460309 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.text.Spanned; +import android.text.SpannableStringBuilder; +import android.text.style.ImageSpan; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.TextView; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Helper class for managing settings preferences that can be disabled + * by device admins via user restrictions. + */ +public class RestrictedPreferenceHelper { + private final Context mContext; + private final Preference mPreference; + private final Drawable mRestrictedPadlock; + private final int mRestrictedPadlockPadding; + + private boolean mDisabledByAdmin; + private EnforcedAdmin mEnforcedAdmin; + private String mAttrUserRestriction = null; + + RestrictedPreferenceHelper(Context context, Preference preference, + AttributeSet attrs) { + mContext = context; + mPreference = preference; + + mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext); + mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.restricted_lock_icon_padding); + + mAttrUserRestriction = attrs.getAttributeValue( + R.styleable.RestrictedPreference_userRestriction); + final TypedArray attributes = context.obtainStyledAttributes(attrs, + R.styleable.RestrictedPreference); + final TypedValue userRestriction = + attributes.peekValue(R.styleable.RestrictedPreference_userRestriction); + CharSequence data = null; + if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) { + if (userRestriction.resourceId != 0) { + data = context.getText(userRestriction.resourceId); + } else { + data = userRestriction.string; + } + } + mAttrUserRestriction = data == null ? null : data.toString(); + } + + /** + * Modify PreferenceViewHolder to add padlock if restriction is disabled. + */ + public void onBindViewHolder(PreferenceViewHolder holder) { + final TextView titleView = (TextView) holder.findViewById(android.R.id.title); + if (titleView != null) { + RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin); + if (mDisabledByAdmin) { + holder.itemView.setEnabled(true); + } + } + } + + /** + * Check if the preference is disabled if so handle the click by informing the user. + * + * @return true if the method handled the click. + */ + public boolean performClick() { + if (mDisabledByAdmin) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin); + return true; + } + return false; + } + + /** + * Disable / enable if we have been passed the restriction in the xml. + */ + protected void onAttachedToHierarchy() { + if (mAttrUserRestriction != null) { + checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId()); + } + } + + /** + * Set the user restriction that is used to disable this preference. + * + * @param userRestriction constant from {@link android.os.UserManager} + * @param userId user to check the restriction for. + */ + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, + userRestriction, userId); + setDisabledByAdmin(admin); + } + + /** + * Disable this preference based on the enforce admin. + * + * @param EnforcedAdmin Details of the admin who enforced the restriction. If it + * is {@code null}, then this preference will be enabled. Otherwise, it will be disabled. + * @return true if the disabled state was changed. + */ + public boolean setDisabledByAdmin(EnforcedAdmin admin) { + final boolean disabled = (admin != null ? true : false); + mEnforcedAdmin = (disabled ? admin : null); + if (mDisabledByAdmin != disabled) { + mDisabledByAdmin = disabled; + mPreference.setEnabled(!disabled); + return true; + } + return false; + } + + public boolean isDisabledByAdmin() { + return mDisabledByAdmin; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java new file mode 100644 index 000000000000..308477b0c84b --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib; + +import android.content.Context; +import android.os.UserHandle; +import android.support.v4.content.res.TypedArrayUtils; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceViewHolder; +import android.support.v14.preference.SwitchPreference; +import android.util.AttributeSet; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +/** + * Version of SwitchPreference that can be disabled by a device admin + * using a user restriction. + */ +public class RestrictedSwitchPreference extends SwitchPreference { + RestrictedPreferenceHelper mHelper; + + public RestrictedSwitchPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mHelper = new RestrictedPreferenceHelper(context, this, attrs); + } + + public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public RestrictedSwitchPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle, + android.R.attr.switchPreferenceStyle)); + } + + public RestrictedSwitchPreference(Context context) { + this(context, null); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mHelper.onBindViewHolder(holder); + } + + @Override + public void performClick() { + if (!mHelper.performClick()) { + super.performClick(); + } + } + + @Override + protected void onAttachedToHierarchy(PreferenceManager preferenceManager) { + mHelper.onAttachedToHierarchy(); + super.onAttachedToHierarchy(preferenceManager); + } + + public void checkRestrictionAndSetDisabled(String userRestriction) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); + } + + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); + } + + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java new file mode 100644 index 000000000000..f1beb103d6c4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.settingslib.users; + +import android.app.AppGlobals; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class AppRestrictionsHelper { + private static final boolean DEBUG = false; + private static final String TAG = "AppRestrictionsHelper"; + + private final Context mContext; + private final PackageManager mPackageManager; + private final IPackageManager mIPm; + private final UserManager mUserManager; + private final UserHandle mUser; + private final boolean mRestrictedProfile; + + HashMap<String,Boolean> mSelectedPackages = new HashMap<>(); + private List<SelectableAppInfo> mVisibleApps; + + public AppRestrictionsHelper(Context context, UserHandle user) { + mContext = context; + mPackageManager = context.getPackageManager(); + mIPm = AppGlobals.getPackageManager(); + mUser = user; + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted(); + } + + public void setPackageSelected(String packageName, boolean selected) { + mSelectedPackages.put(packageName, selected); + } + + public boolean isPackageSelected(String packageName) { + return mSelectedPackages.get(packageName); + } + + public List<SelectableAppInfo> getVisibleApps() { + return mVisibleApps; + } + + public void applyUserAppsStates(OnDisableUiForPackageListener listener) { + final int userId = mUser.getIdentifier(); + if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) { + Log.e(TAG, "Cannot apply application restrictions on another user!"); + return; + } + for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) { + String packageName = entry.getKey(); + boolean enabled = entry.getValue(); + applyUserAppState(packageName, enabled, listener); + } + } + + public void applyUserAppState(String packageName, boolean enabled, + OnDisableUiForPackageListener listener) { + final int userId = mUser.getIdentifier(); + if (enabled) { + // Enable selected apps + try { + ApplicationInfo info = mIPm.getApplicationInfo(packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); + if (info == null || !info.enabled + || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier()); + if (DEBUG) { + Log.d(TAG, "Installing " + packageName); + } + } + if (info != null && (info.privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0 + && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) { + listener.onDisableUiForPackage(packageName); + mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId); + if (DEBUG) { + Log.d(TAG, "Unhiding " + packageName); + } + } + } catch (RemoteException re) { + // Ignore + } + } else { + // Blacklist all other apps, system or downloaded + try { + ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId); + if (info != null) { + if (mRestrictedProfile) { + mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(), + PackageManager.DELETE_SYSTEM_APP); + if (DEBUG) { + Log.d(TAG, "Uninstalling " + packageName); + } + } else { + listener.onDisableUiForPackage(packageName); + mIPm.setApplicationHiddenSettingAsUser(packageName, true, userId); + if (DEBUG) { + Log.d(TAG, "Hiding " + packageName); + } + } + } + } catch (RemoteException re) { + // Ignore + } + } + } + + public void fetchAndMergeApps() { + mVisibleApps = new ArrayList<>(); + final PackageManager pm = mPackageManager; + final IPackageManager ipm = mIPm; + + final HashSet<String> excludePackages = new HashSet<>(); + addSystemImes(excludePackages); + + // Add launchers + Intent launcherIntent = new Intent(Intent.ACTION_MAIN); + launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); + addSystemApps(mVisibleApps, launcherIntent, excludePackages); + + // Add widgets + Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + addSystemApps(mVisibleApps, widgetIntent, excludePackages); + + List<ApplicationInfo> installedApps = pm.getInstalledApplications( + PackageManager.MATCH_UNINSTALLED_PACKAGES); + for (ApplicationInfo app : installedApps) { + // If it's not installed, skip + if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue; + + if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { + // Downloaded app + SelectableAppInfo info = new SelectableAppInfo(); + info.packageName = app.packageName; + info.appName = app.loadLabel(pm); + info.activityName = info.appName; + info.icon = app.loadIcon(pm); + mVisibleApps.add(info); + } else { + try { + PackageInfo pi = pm.getPackageInfo(app.packageName, 0); + // If it's a system app that requires an account and doesn't see restricted + // accounts, mark for removal. It might get shown in the UI if it has an icon + // but will still be marked as false and immutable. + if (mRestrictedProfile + && pi.requiredAccountType != null && pi.restrictedAccountType == null) { + mSelectedPackages.put(app.packageName, false); + } + } catch (PackageManager.NameNotFoundException re) { + // Skip + } + } + } + + // Get the list of apps already installed for the user + List<ApplicationInfo> userApps = null; + try { + ParceledListSlice<ApplicationInfo> listSlice = ipm.getInstalledApplications( + PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier()); + if (listSlice != null) { + userApps = listSlice.getList(); + } + } catch (RemoteException re) { + // Ignore + } + + if (userApps != null) { + for (ApplicationInfo app : userApps) { + if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue; + + if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 + && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { + // Downloaded app + SelectableAppInfo info = new SelectableAppInfo(); + info.packageName = app.packageName; + info.appName = app.loadLabel(pm); + info.activityName = info.appName; + info.icon = app.loadIcon(pm); + mVisibleApps.add(info); + } + } + } + + // Sort the list of visible apps + Collections.sort(mVisibleApps, new AppLabelComparator()); + + // Remove dupes + Set<String> dedupPackageSet = new HashSet<String>(); + for (int i = mVisibleApps.size() - 1; i >= 0; i--) { + SelectableAppInfo info = mVisibleApps.get(i); + if (DEBUG) Log.i(TAG, info.toString()); + String both = info.packageName + "+" + info.activityName; + if (!TextUtils.isEmpty(info.packageName) + && !TextUtils.isEmpty(info.activityName) + && dedupPackageSet.contains(both)) { + mVisibleApps.remove(i); + } else { + dedupPackageSet.add(both); + } + } + + // Establish master/slave relationship for entries that share a package name + HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); + for (SelectableAppInfo info : mVisibleApps) { + if (packageMap.containsKey(info.packageName)) { + info.masterEntry = packageMap.get(info.packageName); + } else { + packageMap.put(info.packageName, info); + } + } + } + + /** + * Find all pre-installed input methods that are marked as default + * and add them to an exclusion list so that they aren't + * presented to the user for toggling. + * Don't add non-default ones, as they may include other stuff that we + * don't need to auto-include. + * @param excludePackages the set of package names to append to + */ + private void addSystemImes(Set<String> excludePackages) { + InputMethodManager imm = (InputMethodManager) + mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + List<InputMethodInfo> imis = imm.getInputMethodList(); + for (InputMethodInfo imi : imis) { + try { + if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) { + excludePackages.add(imi.getPackageName()); + } + } catch (Resources.NotFoundException rnfe) { + // Not default + } + } + } + + /** + * Add system apps that match an intent to the list, excluding any packages in the exclude list. + * @param visibleApps list of apps to append the new list to + * @param intent the intent to match + * @param excludePackages the set of package names to be excluded, since they're required + */ + private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent, + Set<String> excludePackages) { + final PackageManager pm = mPackageManager; + List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent, + PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES); + for (ResolveInfo app : launchableApps) { + if (app.activityInfo != null && app.activityInfo.applicationInfo != null) { + final String packageName = app.activityInfo.packageName; + int flags = app.activityInfo.applicationInfo.flags; + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 + || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + // System app + // Skip excluded packages + if (excludePackages.contains(packageName)) continue; + int enabled = pm.getApplicationEnabledSetting(packageName); + if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED + || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + // Check if the app is already enabled for the target user + ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName, + 0, mUser); + if (targetUserAppInfo == null + || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + continue; + } + } + SelectableAppInfo info = new SelectableAppInfo(); + info.packageName = app.activityInfo.packageName; + info.appName = app.activityInfo.applicationInfo.loadLabel(pm); + info.icon = app.activityInfo.loadIcon(pm); + info.activityName = app.activityInfo.loadLabel(pm); + if (info.activityName == null) info.activityName = info.appName; + + visibleApps.add(info); + } + } + } + } + + private boolean isSystemPackage(String packageName) { + try { + final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); + if (pi.applicationInfo == null) return false; + final int flags = pi.applicationInfo.flags; + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 + || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { + return true; + } + } catch (PackageManager.NameNotFoundException nnfe) { + // Missing package? + } + return false; + } + + private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) { + try { + return mIPm.getApplicationInfo(packageName, flags, user.getIdentifier()); + } catch (RemoteException re) { + return null; + } + } + + public interface OnDisableUiForPackageListener { + void onDisableUiForPackage(String packageName); + } + + public static class SelectableAppInfo { + public String packageName; + public CharSequence appName; + public CharSequence activityName; + public Drawable icon; + public SelectableAppInfo masterEntry; + + @Override + public String toString() { + return packageName + ": appName=" + appName + "; activityName=" + activityName + + "; icon=" + icon + "; masterEntry=" + masterEntry; + } + } + + private static class AppLabelComparator implements Comparator<SelectableAppInfo> { + + @Override + public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) { + String lhsLabel = lhs.activityName.toString(); + String rhsLabel = rhs.activityName.toString(); + return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 443778e525d5..7556c6b33a83 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -474,10 +474,12 @@ public class KeyguardViewMediator extends SystemUI { ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { + @Override public void userActivity() { KeyguardViewMediator.this.userActivity(); } + @Override public void keyguardDone(boolean strongAuth) { if (!mKeyguardDonePending) { KeyguardViewMediator.this.keyguardDone(true /* authenticated */); @@ -487,6 +489,7 @@ public class KeyguardViewMediator extends SystemUI { } } + @Override public void keyguardDoneDrawing() { mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING); } @@ -1248,7 +1251,9 @@ public class KeyguardViewMediator extends SystemUI { if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = " + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence); synchronized (KeyguardViewMediator.this) { - doKeyguardLocked(null); + if (mDelayedShowingSequence == sequence) { + doKeyguardLocked(null); + } } } else if (DELAYED_LOCK_PROFILE_ACTION.equals(intent.getAction())) { int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, 0); @@ -1698,6 +1703,7 @@ public class KeyguardViewMediator extends SystemUI { mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT); } + @Override public void onBootCompleted() { mUpdateMonitor.dispatchBootCompleted(); synchronized (this) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 51cae862cdcc..fdb0d324a081 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -289,7 +289,7 @@ public class RecentsTransitionHelper { // never happen) specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect)); } else { - layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null); + layoutAlgorithm.getStackTransform(t, stackScroll, mTmpTransform, null); specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */)); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index 3c63aae9c416..0901015f12f2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -27,6 +27,7 @@ import android.annotation.SuppressLint; import android.app.Dialog; import android.app.KeyguardManager; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; @@ -559,7 +560,13 @@ public class VolumeDialog { : R.drawable.ic_volume_expand_animation; if (res == mExpandButtonRes) return; mExpandButtonRes = res; - mExpandButton.setImageResource(res); + if (hasTouchFeature()) { + mExpandButton.setImageResource(res); + } else { + // if there is no touch feature, show the volume ringer instead + mExpandButton.setImageResource(R.drawable.ic_volume_ringer); + mExpandButton.setBackgroundResource(0); // remove gray background emphasis + } mExpandButton.setContentDescription(mContext.getString(mExpanded ? R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand)); } @@ -837,6 +844,11 @@ public class VolumeDialog { rescheduleTimeoutH(); } + private boolean hasTouchFeature() { + final PackageManager pm = mContext.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN); + } + private final VolumeDialogController.Callbacks mControllerCallbackH = new VolumeDialogController.Callbacks() { @Override diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java index 28121b41c84f..e31080150280 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java @@ -64,6 +64,14 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen // Indicates that motion events are being collected to match a gesture. private boolean mRecognizingGesture; + // Indicates that motion events from the second pointer are being checked + // for a double tap. + private boolean mSecondFingerDoubleTap; + + // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the + // second pointer. + private long mSecondPointerDownTime; + // Policy flags of the previous event. private int mPolicyFlags; @@ -102,6 +110,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mDoubleTapDetected = false; + mSecondFingerDoubleTap = false; mRecognizingGesture = true; mPreviousX = x; mPreviousY = y; @@ -133,6 +142,43 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen } } break; + + case MotionEvent.ACTION_POINTER_DOWN: + // Once a second finger is used, we're definitely not + // recognizing a gesture. + cancelGesture(); + + if (event.getPointerCount() == 2) { + // If this was the second finger, attempt to recognize double + // taps on it. + mSecondFingerDoubleTap = true; + mSecondPointerDownTime = event.getEventTime(); + } else { + // If there are more than two fingers down, stop watching + // for a double tap. + mSecondFingerDoubleTap = false; + } + break; + + case MotionEvent.ACTION_POINTER_UP: + // If we're detecting taps on the second finger, see if we + // should finish the double tap. + if (mSecondFingerDoubleTap && maybeFinishDoubleTap(event, policyFlags)) { + return true; + } + break; + } + + // If we're detecting taps on the second finger, map events from the + // finger to the first finger. + if (mSecondFingerDoubleTap) { + MotionEvent newEvent = mapSecondPointerToFirstPointer(event); + if (newEvent == null) { + return false; + } + boolean handled = mGestureDetector.onTouchEvent(newEvent); + newEvent.recycle(); + return handled; } if (!mRecognizingGesture) { @@ -146,6 +192,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen public void clear() { mFirstTapDetected = false; mDoubleTapDetected = false; + mSecondFingerDoubleTap = false; cancelGesture(); mStrokeBuffer.clear(); } @@ -229,4 +276,28 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen return false; } + + private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) { + // Only map basic events when two fingers are down. + if (event.getPointerCount() != 2 || + (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN && + event.getActionMasked() != MotionEvent.ACTION_POINTER_UP && + event.getActionMasked() != MotionEvent.ACTION_MOVE)) { + return null; + } + + int action = event.getActionMasked(); + + if (action == MotionEvent.ACTION_POINTER_DOWN) { + action = MotionEvent.ACTION_DOWN; + } else if (action == MotionEvent.ACTION_POINTER_UP) { + action = MotionEvent.ACTION_UP; + } + + // Map the information from the second pointer to the first. + return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action, + event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1), + event.getMetaState(), event.getXPrecision(), event.getYPrecision(), + event.getDeviceId(), event.getEdgeFlags()); + } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 65a27c855921..37a6c0243996 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4848,6 +4848,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override + public String getCaptivePortalServerUrl() { + return NetworkMonitor.getCaptivePortalServerUrl(mContext); + } + + @Override public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger, IBinder binder, String srcAddr, int srcPort, String dstAddr) { enforceKeepalivePermission(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a4b13ed96e00..093a33dc6049 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -35,6 +35,7 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; +import com.android.internal.os.InstallerConnection.InstallerException; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -255,7 +256,9 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +import static android.content.pm.PackageManager.GET_PROVIDERS; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -1306,7 +1309,7 @@ public final class ActivityManagerService extends ActivityManagerNative int mMemWatchDumpUid; String mTrackAllocationApp = null; - final long[] mTmpLong = new long[1]; + final long[] mTmpLong = new long[2]; static final class ProcessChangeItem { static final int CHANGE_ACTIVITIES = 1<<0; @@ -1954,8 +1957,10 @@ public final class ActivityManagerService extends ActivityManagerNative break; } case SYSTEM_USER_UNLOCK_MSG: { - mSystemServiceManager.unlockUser(msg.arg1); - mRecentTasks.cleanupLocked(msg.arg1); + final int userId = msg.arg1; + mSystemServiceManager.unlockUser(userId); + mRecentTasks.cleanupLocked(userId); + installEncryptionUnawareProviders(userId); break; } case SYSTEM_USER_CURRENT_MSG: { @@ -2237,7 +2242,7 @@ public final class ActivityManagerService extends ActivityManagerNative } int num = 0; - long[] tmp = new long[1]; + long[] tmp = new long[2]; do { ProcessRecord proc; int procState; @@ -2269,7 +2274,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (pss != 0 && proc.thread != null && proc.setProcState == procState && proc.pid == pid && proc.lastPssTime == lastPssTime) { num++; - recordPssSampleLocked(proc, procState, pss, tmp[0], + recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], SystemClock.uptimeMillis()); } } @@ -6586,8 +6591,10 @@ public final class ActivityManagerService extends ActivityManagerNative Process.establishZygoteConnectionForAbi(abi); final String instructionSet = VMRuntime.getInstructionSet(abi); if (!completedIsas.contains(instructionSet)) { - if (mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi)) != 0) { - Slog.e(TAG, "Unable to mark boot complete for abi: " + abi); + try { + mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi)); + } catch (InstallerException e) { + Slog.e(TAG, "Unable to mark boot complete for abi: " + abi, e); } completedIsas.add(instructionSet); } @@ -10824,6 +10831,49 @@ public final class ActivityManagerService extends ActivityManagerNative } /** + * When a user is unlocked, we need to install encryption-unaware providers + * belonging to any running apps. + */ + private void installEncryptionUnawareProviders(int userId) { + if (!StorageManager.isFileBasedEncryptionEnabled()) { + // TODO: eventually pivot this back to look at current user state, + // similar to the comment in UserManager.isUserUnlocked(), but for + // now, if we started apps when "unlocked" then unaware providers + // have already been spun up. + return; + } + + synchronized (this) { + final int NP = mProcessNames.getMap().size(); + for (int ip = 0; ip < NP; ip++) { + final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); + final int NA = apps.size(); + for (int ia = 0; ia < NA; ia++) { + final ProcessRecord app = apps.valueAt(ia); + if (app.userId != userId || app.thread == null) continue; + + final int NG = app.pkgList.size(); + for (int ig = 0; ig < NG; ig++) { + try { + final String pkgName = app.pkgList.keyAt(ig); + final PackageInfo pkgInfo = AppGlobals.getPackageManager() + .getPackageInfo(pkgName, + GET_PROVIDERS | MATCH_ENCRYPTION_UNAWARE, userId); + if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) { + for (ProviderInfo provInfo : pkgInfo.providers) { + Log.v(TAG, "Installing " + provInfo); + app.thread.scheduleInstallProvider(provInfo); + } + } + } catch (RemoteException ignored) { + } + } + } + } + } + } + + /** * Allows apps to retrieve the MIME type of a URI. * If an app is in the same user as the ContentProvider, or if it is allowed to interact across * users, then it does not need permission to access the ContentProvider. @@ -12220,6 +12270,8 @@ public final class ActivityManagerService extends ActivityManagerNative sb.append(proc.processName); sb.append(" in idle maint: pss="); sb.append(proc.lastPss); + sb.append(", swapPss="); + sb.append(proc.lastSwapPss); sb.append(", initialPss="); sb.append(proc.initialIdlePss); sb.append(", period="); @@ -15127,6 +15179,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState)); pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState)); pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024); + pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024); pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024); pw.println(); pw.print(prefix); @@ -15280,32 +15333,35 @@ public final class ActivityManagerService extends ActivityManagerNative final String label; final String shortLabel; final long pss; + final long swapPss; final int id; final boolean hasActivities; ArrayList<MemItem> subitems; - public MemItem(String _label, String _shortLabel, long _pss, int _id, + public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id, boolean _hasActivities) { isProc = true; label = _label; shortLabel = _shortLabel; pss = _pss; + swapPss = _swapPss; id = _id; hasActivities = _hasActivities; } - public MemItem(String _label, String _shortLabel, long _pss, int _id) { + public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) { isProc = false; label = _label; shortLabel = _shortLabel; pss = _pss; + swapPss = _swapPss; id = _id; hasActivities = false; } } static final void dumpMemItems(PrintWriter pw, String prefix, String tag, - ArrayList<MemItem> items, boolean sort, boolean isCompact) { + ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) { if (sort && !isCompact) { Collections.sort(items, new Comparator<MemItem>() { @Override @@ -15323,18 +15379,24 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<items.size(); i++) { MemItem mi = items.get(i); if (!isCompact) { - pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label); + if (dumpSwapPss) { + pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss), + mi.label, stringifyKBSize(mi.swapPss)); + } else { + pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label); + } } else if (mi.isProc) { pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel); - pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); + pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); pw.print(","); + pw.print(dumpSwapPss ? mi.swapPss : "N/A"); pw.println(mi.hasActivities ? ",a" : ",e"); } else { pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(","); - pw.println(mi.pss); + pw.println(mi.pss); pw.print(dumpSwapPss ? mi.swapPss : "N/A"); } if (mi.subitems != null) { - dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems, - true, isCompact); + dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems, + true, isCompact, dumpSwapPss); } } } @@ -15463,6 +15525,8 @@ public final class ActivityManagerService extends ActivityManagerNative boolean isCompact = false; boolean localOnly = false; boolean packages = false; + boolean isCheckinRequest = false; + boolean dumpSwapPss = false; int opti = 0; while (opti < args.length) { @@ -15475,6 +15539,7 @@ public final class ActivityManagerService extends ActivityManagerNative dumpDetails = true; dumpFullDetails = true; dumpDalvik = true; + dumpSwapPss = true; } else if ("-d".equals(opt)) { dumpDalvik = true; } else if ("-c".equals(opt)) { @@ -15482,22 +15547,29 @@ public final class ActivityManagerService extends ActivityManagerNative } else if ("-s".equals(opt)) { dumpDetails = true; dumpSummaryOnly = true; + } else if ("-S".equals(opt)) { + dumpSwapPss = true; } else if ("--oom".equals(opt)) { oomOnly = true; } else if ("--local".equals(opt)) { localOnly = true; } else if ("--package".equals(opt)) { packages = true; + } else if ("--checkin".equals(opt)) { + isCheckinRequest = true; + } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]"); pw.println(" -a: include all available information for each process."); pw.println(" -d: include dalvik details."); pw.println(" -c: dump in a compact machine-parseable representation."); pw.println(" -s: dump only summary of application memory usage."); + pw.println(" -S: dump also SwapPss."); pw.println(" --oom: only show processes organized by oom adj."); pw.println(" --local: only collect details locally, don't call process."); pw.println(" --package: interpret process arg as package, dumping all"); pw.println(" processes that have loaded that package."); + pw.println(" --checkin: dump data for a checkin"); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; @@ -15506,7 +15578,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final boolean isCheckinRequest = scanArgs(args, "--checkin"); long uptime = SystemClock.uptimeMillis(); long realtime = SystemClock.elapsedRealtime(); final long[] tmpLong = new long[1]; @@ -15578,18 +15649,28 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> procMems = new ArrayList<MemItem>(); final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); long nativePss = 0; + long nativeSwapPss = 0; long dalvikPss = 0; + long dalvikSwapPss = 0; long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG; + long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : + EmptyArray.LONG; long otherPss = 0; + long otherSwapPss = 0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; + long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; + long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length]; ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) new ArrayList[DUMP_MEM_OOM_LABEL.length]; long totalPss = 0; + long totalSwapPss = 0; long cachedPss = 0; + long cachedSwapPss = 0; + boolean hasSwapPss = false; Debug.MemoryInfo mi = null; for (int i = procs.size() - 1 ; i >= 0 ; i--) { @@ -15613,6 +15694,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (dumpDetails || (!brief && !oomOnly)) { Debug.getMemoryInfo(pid, mi); + hasSwapPss = mi.hasSwappedOutPss; } else { mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); mi.dalvikPrivateDirty = (int)tmpLong[0]; @@ -15640,6 +15722,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long myTotalPss = mi.getTotalPss(); final long myTotalUss = mi.getTotalUss(); + final long myTotalSwapPss = mi.getTotalSwappedOutPss(); synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { @@ -15650,32 +15733,43 @@ public final class ActivityManagerService extends ActivityManagerNative if (!isCheckinRequest && mi != null) { totalPss += myTotalPss; + totalSwapPss += myTotalSwapPss; MemItem pssItem = new MemItem(r.processName + " (pid " + pid + - (hasActivities ? " / activities)" : ")"), - r.processName, myTotalPss, pid, hasActivities); + (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, + myTotalSwapPss, pid, hasActivities); procMems.add(pssItem); procMemsMap.put(pid, pssItem); nativePss += mi.nativePss; + nativeSwapPss += mi.nativeSwappedOutPss; dalvikPss += mi.dalvikPss; + dalvikSwapPss += mi.dalvikSwappedOutPss; for (int j=0; j<dalvikSubitemPss.length; j++) { dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikSubitemSwapPss[j] += + mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); } otherPss += mi.otherPss; + otherSwapPss += mi.otherSwappedOutPss; for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = mi.getOtherPss(j); miscPss[j] += mem; otherPss -= mem; + mem = mi.getOtherSwappedOutPss(j); + miscSwapPss[j] += mem; + otherSwapPss -= mem; } if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { cachedPss += myTotalPss; + cachedSwapPss += myTotalSwapPss; } for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex] || oomIndex == (oomPss.length-1)) { oomPss[oomIndex] += myTotalPss; + oomSwapPss[oomIndex] += myTotalSwapPss; if (oomProcs[oomIndex] == null) { oomProcs[oomIndex] = new ArrayList<MemItem>(); } @@ -15710,26 +15804,35 @@ public final class ActivityManagerService extends ActivityManagerNative } final long myTotalPss = mi.getTotalPss(); + final long myTotalSwapPss = mi.getTotalSwappedOutPss(); totalPss += myTotalPss; nativeProcTotalPss += myTotalPss; MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", - st.name, myTotalPss, st.pid, false); + st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false); procMems.add(pssItem); nativePss += mi.nativePss; + nativeSwapPss += mi.nativeSwappedOutPss; dalvikPss += mi.dalvikPss; + dalvikSwapPss += mi.dalvikSwappedOutPss; for (int j=0; j<dalvikSubitemPss.length; j++) { - dalvikSubitemPss[j] += mi.getOtherPss( - Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); + dalvikSubitemSwapPss[j] += + mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); } otherPss += mi.otherPss; + otherSwapPss += mi.otherSwappedOutPss; for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { long mem = mi.getOtherPss(j); miscPss[j] += mem; otherPss -= mem; + mem = mi.getOtherSwappedOutPss(j); + miscSwapPss[j] += mem; + otherSwapPss -= mem; } oomPss[0] += myTotalPss; + oomSwapPss[0] += myTotalSwapPss; if (oomProcs[0] == null) { oomProcs[0] = new ArrayList<MemItem>(); } @@ -15740,21 +15843,23 @@ public final class ActivityManagerService extends ActivityManagerNative ArrayList<MemItem> catMems = new ArrayList<MemItem>(); - catMems.add(new MemItem("Native", "Native", nativePss, -1)); - final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2); + catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1)); + final MemItem dalvikItem = + new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2); if (dalvikSubitemPss.length > 0) { dalvikItem.subitems = new ArrayList<MemItem>(); for (int j=0; j<dalvikSubitemPss.length; j++) { final String name = Debug.MemoryInfo.getOtherLabel( Debug.MemoryInfo.NUM_OTHER_STATS + j); - dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j)); + dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], + dalvikSubitemSwapPss[j], j)); } } catMems.add(dalvikItem); - catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3)); + catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { String label = Debug.MemoryInfo.getOtherLabel(j); - catMems.add(new MemItem(label, label, miscPss[j], j)); + catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j)); } ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); @@ -15762,30 +15867,31 @@ public final class ActivityManagerService extends ActivityManagerNative if (oomPss[j] != 0) { String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] : DUMP_MEM_OOM_LABEL[j]; - MemItem item = new MemItem(label, label, oomPss[j], + MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; oomMems.add(item); } } + dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0; if (!brief && !oomOnly && !isCompact) { pw.println(); pw.println("Total PSS by process:"); - dumpMemItems(pw, " ", "proc", procMems, true, isCompact); + dumpMemItems(pw, " ", "proc", procMems, true, isCompact, dumpSwapPss); pw.println(); } if (!isCompact) { pw.println("Total PSS by OOM adjustment:"); } - dumpMemItems(pw, " ", "oom", oomMems, false, isCompact); + dumpMemItems(pw, " ", "oom", oomMems, false, isCompact, dumpSwapPss); if (!brief && !oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; if (!isCompact) { out.println(); out.println("Total PSS by category:"); } - dumpMemItems(out, " ", "cat", catMems, true, isCompact); + dumpMemItems(out, " ", "cat", catMems, true, isCompact, dumpSwapPss); } if (!isCompact) { pw.println(); @@ -18291,9 +18397,12 @@ public final class ActivityManagerService extends ActivityManagerNative * dialog / global actions also might want different behaviors. */ private static final boolean shouldShowDialogs(Configuration config) { - return !(config.keyboard == Configuration.KEYBOARD_NOKEYS - && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH - && config.navigation == Configuration.NAVIGATION_NONAV); + final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS + && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH + && config.navigation == Configuration.NAVIGATION_NONAV); + final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK) + == Configuration.UI_MODE_TYPE_CAR); + return inputMethodExists && uiIsNotCarType; } @Override @@ -19105,8 +19214,10 @@ public final class ActivityManagerService extends ActivityManagerNative /** * Record new PSS sample for a process. */ - void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long now) { - EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024); + void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss, + long now) { + EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024, + swapPss * 1024); proc.lastPssTime = now; proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList); if (DEBUG_PSS) Slog.d(TAG_PSS, @@ -19116,8 +19227,10 @@ public final class ActivityManagerService extends ActivityManagerNative proc.initialIdlePss = pss; } proc.lastPss = pss; + proc.lastSwapPss = swapPss; if (procState >= ActivityManager.PROCESS_STATE_HOME) { proc.lastCachedPss = pss; + proc.lastCachedSwapPss = swapPss; } final SparseArray<Pair<Long, String>> watchUids @@ -19553,7 +19666,7 @@ public final class ActivityManagerService extends ActivityManagerNative // states, which well tend to give noisy data. long start = SystemClock.uptimeMillis(); long pss = Debug.getPss(app.pid, mTmpLong, null); - recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], now); + recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now); mPendingPssProcesses.remove(app); Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState + " to " + app.curProcState + ": " diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 4fb87c3a2660..eb0945bfe5f1 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -171,7 +171,9 @@ final class ActivityRecord { boolean stopped; // is activity pause finished? boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? - boolean configDestroy; // need to destroy due to config change? + boolean deferRelaunchUntilPaused; // relaunch of activity is being deferred until pause is + // completed + boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch int configChangeFlags; // which config values have changed boolean keysPaused; // has key dispatching been paused for it? int launchMode; // the launch mode activity attribute. @@ -184,9 +186,9 @@ final class ActivityRecord { boolean immersive; // immersive mode (don't interrupt if possible) boolean forceNewConfig; // force re-create with new config next time int launchCount; // count of launches since last state - long lastLaunchTime; // time of last lauch of this activity + long lastLaunchTime; // time of last launch of this activity boolean isVrActivity; // is the activity running in VR mode? - ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>(); + ArrayList<ActivityContainer> mChildContainers = new ArrayList<>(); String stringName; // for caching of toString(). @@ -341,8 +343,8 @@ final class ActivityRecord { else TimeUtils.formatDuration(lastVisibleTime, now, pw); pw.println(); } - if (configDestroy || configChangeFlags != 0) { - pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy); + if (deferRelaunchUntilPaused || configChangeFlags != 0) { + pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused); pw.print(" configChangeFlags="); pw.println(Integer.toHexString(configChangeFlags)); } @@ -551,7 +553,7 @@ final class ActivityRecord { stopped = false; delayedResume = false; finishing = false; - configDestroy = false; + deferRelaunchUntilPaused = false; keysPaused = false; inHistory = false; visible = false; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index c44b4cfa7b26..312e309e3950 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1088,7 +1088,7 @@ final class ActivityStack { if (r.finishing) { r.clearOptionsLocked(); } else { - if (r.configDestroy) { + if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-config"); mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { @@ -1114,14 +1114,11 @@ final class ActivityStack { if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause, no longer waiting: " + prev); } - if (prev.configDestroy) { - // The previous is being paused because the configuration - // is changing, which means it is actually stopping... - // To juggle the fact that we are also starting a new - // instance right now, we need to first completely stop - // the current instance before starting the new one. - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true, "pause-config"); + if (prev.deferRelaunchUntilPaused) { + // Complete the deferred relaunch that was waiting for pause to complete. + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev); + relaunchActivityLocked(prev, prev.configChangeFlags, false, + prev.preserveWindowOnDeferredRelaunch); } else if (wasStopping) { // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. @@ -2318,11 +2315,12 @@ final class ActivityStack { ActivityStack lastStack = mStackSupervisor.getLastStack(); final boolean fromHome = lastStack.isHomeStack(); if (!isHomeStack() && (fromHome || topTask() != task)) { - task.setTaskToReturnTo(fromHome - ? lastStack.topTask() == null - ? HOME_ACTIVITY_TYPE - : lastStack.topTask().taskType - : APPLICATION_ACTIVITY_TYPE); + int returnToType = APPLICATION_ACTIVITY_TYPE; + if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) { + returnToType = lastStack.topTask() == null + ? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType; + } + task.setTaskToReturnTo(returnToType); } } else { task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); @@ -2972,7 +2970,7 @@ final class ActivityStack { r.stopped = true; if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r); r.state = ActivityState.STOPPED; - if (r.configDestroy) { + if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-except"); } } @@ -3403,7 +3401,7 @@ final class ActivityStack { } mService.resetFocusedActivityIfNeededLocked(r); - r.configDestroy = false; + r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; if (setState) { @@ -4175,38 +4173,33 @@ final class ActivityStack { r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); r.forceNewConfig = false; + preserveWindow &= isResizeOnlyChange(changes); if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is destroying non-running " + r); destroyActivityLocked(r, true, "config"); } else if (r.state == ActivityState.PAUSING) { - // A little annoying: we are waiting for this activity to - // finish pausing. Let's not do anything now, but just - // flag that it needs to be restarted when done pausing. + // A little annoying: we are waiting for this activity to finish pausing. Let's not + // do anything now, but just flag that it needs to be restarted when done pausing. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is skipping already pausing " + r); - r.configDestroy = true; + r.deferRelaunchUntilPaused = true; + r.preserveWindowOnDeferredRelaunch = preserveWindow; return true; } else if (r.state == ActivityState.RESUMED) { - // Try to optimize this case: the configuration is changing - // and we need to restart the top, resumed activity. - // Instead of doing the normal handshaking, just say + // Try to optimize this case: the configuration is changing and we need to restart + // the top, resumed activity. Instead of doing the normal handshaking, just say // "restart!". if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching resumed " + r); - relaunchActivityLocked(r, r.configChangeFlags, true, - preserveWindow && isResizeOnlyChange(changes)); - r.configChangeFlags = 0; + relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow); } else { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching non-resumed " + r); - relaunchActivityLocked(r, r.configChangeFlags, false, - preserveWindow && isResizeOnlyChange(changes)); - r.configChangeFlags = 0; + relaunchActivityLocked(r, r.configChangeFlags, false, preserveWindow); } - // All done... tell the caller we weren't able to keep this - // activity around. + // All done... tell the caller we weren't able to keep this activity around. return false; } @@ -4298,6 +4291,7 @@ final class ActivityStack { private void relaunchActivityLocked( ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) { if (mService.mSuppressResizeConfigChanges && preserveWindow) { + r.configChangeFlags = 0; return; } @@ -4341,6 +4335,8 @@ final class ActivityStack { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); r.state = ActivityState.PAUSED; } + + r.configChangeFlags = 0; } boolean willActivityBeVisibleLocked(IBinder token) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 4672023a220e..c634e0e6a6ac 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2722,7 +2722,7 @@ public final class ActivityStackSupervisor implements DisplayListener { for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord s = mStoppingActivities.get(activityNdx); final boolean waitingVisible = mWaitingVisibleActivities.contains(s); - if (DEBUG_ALL) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible + " waitingVisible=" + waitingVisible + " finishing=" + s.finishing); if (waitingVisible && nowVisible) { mWaitingVisibleActivities.remove(s); @@ -2732,12 +2732,12 @@ public final class ActivityStackSupervisor implements DisplayListener { // so get rid of it. Otherwise, we need to go through the // normal flow and hide it once we determine that it is // hidden by the activities in front of it. - if (DEBUG_ALL) Slog.v(TAG, "Before stopping, can hide: " + s); + if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s); mWindowManager.setAppVisibility(s.appToken, false); } } if ((!waitingVisible || mService.isSleepingOrShuttingDown()) && remove) { - if (DEBUG_ALL) Slog.v(TAG, "Ready to stop: " + s); + if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s); if (stops == null) { stops = new ArrayList<>(); } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 03975536816d..f2e8d090d85d 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -100,6 +100,6 @@ option java_package com.android.server.am 30045 am_pre_boot (User|1|5),(Package|3) # Report collection of global memory state -30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2) +30046 am_meminfo (Cached|2|2),(Free|2|2),(Zram|2|2),(Kernel|2|2),(Native|2|2) # Report collection of memory used by a process -30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2) +30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2) diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 4bfe30075cff..b4aa4cfe84ec 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -82,7 +82,9 @@ final class ProcessRecord { long lastStateTime; // Last time setProcState changed long initialIdlePss; // Initial memory pss of process for idle maintenance. long lastPss; // Last computed memory pss. + long lastSwapPss; // Last computed SwapPss. long lastCachedPss; // Last computed pss when in cached state. + long lastCachedSwapPss; // Last computed SwapPss when in cached state. int maxAdj; // Maximum OOM adjustment for this process int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process @@ -257,7 +259,9 @@ final class ProcessRecord { pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.print(lruSeq); pw.print(" lastPss="); DebugUtils.printSizeValue(pw, lastPss*1024); + pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024); pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024); + pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024); pw.println(); pw.print(prefix); pw.print("cached="); pw.print(cached); pw.print(" empty="); pw.println(empty); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index b8cbecb48928..9331dd8cd92e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -58,10 +58,12 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioPort; +import android.media.AudioRecordConfiguration; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; import android.media.IAudioService; +import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.IVolumeController; import android.media.MediaPlayer; @@ -706,6 +708,8 @@ public class AudioService extends IAudioService.Stub { LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + + mRecordMonitor.initMonitor(); } public void systemReady() { @@ -6165,7 +6169,7 @@ public class AudioService extends IAudioService.Stub { } //====================== - // Audio policy callback from AudioSystem + // Audio policy callbacks from AudioSystem for dynamic policies //====================== private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback = new AudioSystem.DynamicPolicyCallback() { @@ -6194,7 +6198,23 @@ public class AudioService extends IAudioService.Stub { } } } + } + + //====================== + // Audio policy callbacks from AudioSystem for recording configuration updates + //====================== + private final RecordingActivityMonitor mRecordMonitor = new RecordingActivityMonitor(); + + public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) { + mRecordMonitor.registerRecordingCallback(rcdb); + } + + public void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) { + mRecordMonitor.unregisterRecordingCallback(rcdb); + } + public AudioRecordConfiguration[] getActiveRecordConfigurations() { + return mRecordMonitor.getActiveRecordConfigurations(); } //====================== diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java new file mode 100644 index 000000000000..5806f3fb5b70 --- /dev/null +++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.audio; + +import android.media.AudioManager; +import android.media.AudioRecordConfiguration; +import android.media.AudioSystem; +import android.media.IRecordingConfigDispatcher; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +/** + * Class to receive and dispatch updates from AudioSystem about recording configurations. + */ +public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback { + + public final static String TAG = "AudioService.RecordingActivityMonitor"; + + private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>(); + + private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs = + new HashMap<Integer, AudioRecordConfiguration>(); + + RecordingActivityMonitor() { + RecMonitorClient.sMonitor = this; + } + + /** + * Implementation of android.media.AudioSystem.AudioRecordingCallback + */ + public void onRecordingConfigurationChanged(int event, int session, int source) { + if (updateSnapshot(event, session, source)) { + final Iterator<RecMonitorClient> clientIterator = mClients.iterator(); + synchronized(mClients) { + while (clientIterator.hasNext()) { + try { + clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange(); + } catch (RemoteException e) { + Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e); + } + } + } + } + } + + void initMonitor() { + AudioSystem.setRecordingCallback(this); + } + + void registerRecordingCallback(IRecordingConfigDispatcher rcdb) { + if (rcdb == null) { + return; + } + synchronized(mClients) { + final RecMonitorClient rmc = new RecMonitorClient(rcdb); + if (rmc.init()) { + mClients.add(rmc); + } + } + } + + void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) { + if (rcdb == null) { + return; + } + synchronized(mClients) { + final Iterator<RecMonitorClient> clientIterator = mClients.iterator(); + while (clientIterator.hasNext()) { + RecMonitorClient rmc = clientIterator.next(); + if (rcdb.equals(rmc.mDispatcherCb)) { + rmc.release(); + clientIterator.remove(); + break; + } + } + } + } + + AudioRecordConfiguration[] getActiveRecordConfigurations() { + synchronized(mRecordConfigs) { + return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]); + } + } + + /** + * Update the internal "view" of the active recording sessions + * @param event + * @param session + * @param source + * @return true if the list of active recording sessions has been modified, false otherwise. + */ + private boolean updateSnapshot(int event, int session, int source) { + synchronized(mRecordConfigs) { + switch (event) { + case AudioManager.RECORD_CONFIG_EVENT_STOP: + // return failure if an unknown recording session stopped + return (mRecordConfigs.remove(new Integer(session)) != null); + case AudioManager.RECORD_CONFIG_EVENT_START: + if (mRecordConfigs.containsKey(new Integer(session))) { + // start of session that's already tracked, not worth an update + // TO DO in the future when tracking record format: there might be a record + // format change during a recording that requires reporting + return false; + } else { + mRecordConfigs.put(new Integer(session), + new AudioRecordConfiguration(session, source)); + return true; + } + default: + Log.e(TAG, String.format("Unknown event %d for session %d, source %d", + event, session, source)); + return false; + } + } + } + + /** + * Inner class to track clients that want to be notified of recording updates + */ + private final static class RecMonitorClient implements IBinder.DeathRecipient { + + // can afford to be static because only one RecordingActivityMonitor ever instantiated + static RecordingActivityMonitor sMonitor; + + final IRecordingConfigDispatcher mDispatcherCb; + + RecMonitorClient(IRecordingConfigDispatcher rcdb) { + mDispatcherCb = rcdb; + } + + public void binderDied() { + Log.w(TAG, "client died"); + sMonitor.unregisterRecordingCallback(mDispatcherCb); + } + + boolean init() { + try { + mDispatcherCb.asBinder().linkToDeath(this, 0); + return true; + } catch (RemoteException e) { + Log.w(TAG, "Could not link to client death", e); + return false; + } + } + + void release() { + mDispatcherCb.asBinder().unlinkToDeath(this, 0); + } + } +} diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 3a10dbe3fc50..4504bdb08945 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -223,7 +223,6 @@ public class NetworkMonitor extends StateMachine { private final AlarmManager mAlarmManager; private final NetworkRequest mDefaultRequest; - private String mServer; private boolean mIsCaptivePortalCheckEnabled = false; // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. @@ -265,10 +264,6 @@ public class NetworkMonitor extends StateMachine { addState(mLingeringState, mDefaultState); setInitialState(mDefaultState); - mServer = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.CAPTIVE_PORTAL_SERVER); - if (mServer == null) mServer = DEFAULT_SERVER; - mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), @@ -622,6 +617,13 @@ public class NetworkMonitor extends StateMachine { } } + public static String getCaptivePortalServerUrl(Context context) { + String server = Settings.Global.getString(context.getContentResolver(), + Settings.Global.CAPTIVE_PORTAL_SERVER); + if (server == null) server = DEFAULT_SERVER; + return "http://" + server + "/generate_204"; + } + /** * Do a URL fetch on a known server to see if we get the data we expect. * Returns HTTP response code. @@ -633,9 +635,9 @@ public class NetworkMonitor extends StateMachine { HttpURLConnection urlConnection = null; int httpResponseCode = 599; try { - URL url = new URL("http", mServer, "/generate_204"); + URL url = new URL(getCaptivePortalServerUrl(mContext)); // On networks with a PAC instead of fetching a URL that should result in a 204 - // reponse, we instead simply fetch the PAC script. This is done for a few reasons: + // response, we instead simply fetch the PAC script. This is done for a few reasons: // 1. At present our PAC code does not yet handle multiple PACs on multiple networks // until something like https://android-review.googlesource.com/#/c/115180/ lands. // Network.openConnection() will ignore network-specific PACs and instead fetch @@ -644,7 +646,8 @@ public class NetworkMonitor extends StateMachine { // 2. To proxy the generate_204 fetch through a PAC would require a number of things // happen before the fetch can commence, namely: // a) the PAC script be fetched - // b) a PAC script resolver service be fired up and resolve mServer + // b) a PAC script resolver service be fired up and resolve the captive portal + // server. // Network validation could be delayed until these prerequisities are satisifed or // could simply be left to race them. Neither is an optimal solution. // 3. PAC scripts are sometimes used to block or restrict Internet access and may in diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 99a051af1717..190eca63b183 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -20,14 +20,14 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageStats; import android.os.Build; -import android.text.TextUtils; import android.util.Slog; -import dalvik.system.VMRuntime; - import com.android.internal.os.InstallerConnection; +import com.android.internal.os.InstallerConnection.InstallerException; import com.android.server.SystemService; +import dalvik.system.VMRuntime; + public final class Installer extends SystemService { private static final String TAG = "Installer"; @@ -46,6 +46,11 @@ public final class Installer extends SystemService { /** Run the application with the JIT compiler */ public static final int DEXOPT_USEJIT = 1 << 5; + public static final int FLAG_DE_STORAGE = 1 << 0; + public static final int FLAG_CE_STORAGE = 1 << 1; + public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2; + public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3; + private final InstallerConnection mInstaller; public Installer(Context context) { @@ -67,423 +72,137 @@ public final class Installer extends SystemService { mInstaller.waitForConnection(); } - private static String escapeNull(String arg) { - if (TextUtils.isEmpty(arg)) { - return "!"; - } else { - if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) { - throw new IllegalArgumentException(arg); - } - return arg; - } - } - - @Deprecated - public int install(String name, int uid, int gid, String seinfo) { - return install(null, name, uid, gid, seinfo); - } - - public int install(String uuid, String name, int uid, int gid, String seinfo) { - StringBuilder builder = new StringBuilder("install"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(uid); - builder.append(' '); - builder.append(gid); - builder.append(' '); - builder.append(seinfo != null ? seinfo : "!"); - return mInstaller.execute(builder.toString()); - } - - public int dexopt(String apkPath, int uid, String instructionSet, - int dexoptNeeded, int dexFlags) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags); - } - - public int dexopt(String apkPath, int uid, String pkgName, String instructionSet, - int dexoptNeeded, @Nullable String outputPath, int dexFlags) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, - outputPath, dexFlags); - } - - public int idmap(String targetApkPath, String overlayApkPath, int uid) { - StringBuilder builder = new StringBuilder("idmap"); - builder.append(' '); - builder.append(targetApkPath); - builder.append(' '); - builder.append(overlayApkPath); - builder.append(' '); - builder.append(uid); - return mInstaller.execute(builder.toString()); - } - - public int movedex(String srcPath, String dstPath, String instructionSet) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - StringBuilder builder = new StringBuilder("movedex"); - builder.append(' '); - builder.append(srcPath); - builder.append(' '); - builder.append(dstPath); - builder.append(' '); - builder.append(instructionSet); - return mInstaller.execute(builder.toString()); - } - - public int rmdex(String codePath, String instructionSet) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - StringBuilder builder = new StringBuilder("rmdex"); - builder.append(' '); - builder.append(codePath); - builder.append(' '); - builder.append(instructionSet); - return mInstaller.execute(builder.toString()); - } - - /** - * Removes packageDir or its subdirectory - */ - public int rmPackageDir(String packageDir) { - StringBuilder builder = new StringBuilder("rmpackagedir"); - builder.append(' '); - builder.append(packageDir); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int remove(String name, int userId) { - return remove(null, name, userId); - } - - public int remove(String uuid, String name, int userId) { - StringBuilder builder = new StringBuilder("remove"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int fixUid(String name, int uid, int gid) { - return fixUid(null, name, uid, gid); - } - - public int fixUid(String uuid, String name, int uid, int gid) { - StringBuilder builder = new StringBuilder("fixuid"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(uid); - builder.append(' '); - builder.append(gid); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int deleteCacheFiles(String name, int userId) { - return deleteCacheFiles(null, name, userId); - } - - public int deleteCacheFiles(String uuid, String name, int userId) { - StringBuilder builder = new StringBuilder("rmcache"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int deleteCodeCacheFiles(String name, int userId) { - return deleteCodeCacheFiles(null, name, userId); - } - - public int deleteCodeCacheFiles(String uuid, String name, int userId) { - StringBuilder builder = new StringBuilder("rmcodecache"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int createUserData(String name, int uid, int userId, String seinfo) { - return createUserData(null, name, uid, userId, seinfo); - } - - public int createUserData(String uuid, String name, int uid, int userId, String seinfo) { - StringBuilder builder = new StringBuilder("mkuserdata"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(uid); - builder.append(' '); - builder.append(userId); - builder.append(' '); - builder.append(seinfo != null ? seinfo : "!"); - return mInstaller.execute(builder.toString()); - } - - public int createUserConfig(int userId) { - StringBuilder builder = new StringBuilder("mkuserconfig"); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int removeUserDataDirs(int userId) { - return removeUserDataDirs(null, userId); - } - - public int removeUserDataDirs(String uuid, int userId) { - StringBuilder builder = new StringBuilder("rmuser"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); - } - - public int copyCompleteApp(String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seinfo) { - StringBuilder builder = new StringBuilder("cpcompleteapp"); - builder.append(' '); - builder.append(escapeNull(fromUuid)); - builder.append(' '); - builder.append(escapeNull(toUuid)); - builder.append(' '); - builder.append(packageName); - builder.append(' '); - builder.append(dataAppName); - builder.append(' '); - builder.append(appId); - builder.append(' '); - builder.append(seinfo); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int clearUserData(String name, int userId) { - return clearUserData(null, name, userId); + public void createAppData(String uuid, String pkgname, int userid, int flags, int appid, + String seinfo) throws InstallerException { + mInstaller.execute("create_app_data", uuid, pkgname, userid, flags, appid, seinfo); } - public int clearUserData(String uuid, String name, int userId) { - StringBuilder builder = new StringBuilder("rmuserdata"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(name); - builder.append(' '); - builder.append(userId); - return mInstaller.execute(builder.toString()); + public void restoreconAppData(String uuid, String pkgname, int userid, int flags, int appid, + String seinfo) throws InstallerException { + mInstaller.execute("restorecon_app_data", uuid, pkgname, userid, flags, appid, + seinfo); } - public int markBootComplete(String instructionSet) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } - - StringBuilder builder = new StringBuilder("markbootcomplete"); - builder.append(' '); - builder.append(instructionSet); - return mInstaller.execute(builder.toString()); - } - - @Deprecated - public int freeCache(long freeStorageSize) { - return freeCache(null, freeStorageSize); + public void clearAppData(String uuid, String pkgname, int userid, int flags) + throws InstallerException { + mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags); } - public int freeCache(String uuid, long freeStorageSize) { - StringBuilder builder = new StringBuilder("freecache"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(String.valueOf(freeStorageSize)); - return mInstaller.execute(builder.toString()); + public void destroyAppData(String uuid, String pkgname, int userid, int flags) + throws InstallerException { + mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags); } - @Deprecated - public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath, - String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) { - return getSizeInfo(null, pkgName, persona, apkPath, libDirPath, fwdLockApkPath, asecPath, - instructionSets, pStats); + public void moveCompleteApp(String from_uuid, String to_uuid, String package_name, + String data_app_name, int appid, String seinfo) throws InstallerException { + mInstaller.execute("move_complete_app", from_uuid, to_uuid, package_name, + data_app_name, appid, seinfo); } - public int getSizeInfo(String uuid, String pkgName, int persona, String apkPath, + public void getAppSize(String uuid, String pkgname, int userid, int flags, String apkPath, String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets, - PackageStats pStats) { + PackageStats pStats) throws InstallerException { for (String instructionSet : instructionSets) { - if (!isValidInstructionSet(instructionSet)) { - Slog.e(TAG, "Invalid instruction set: " + instructionSet); - return -1; - } + assertValidInstructionSet(instructionSet); } - StringBuilder builder = new StringBuilder("getsize"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(pkgName); - builder.append(' '); - builder.append(persona); - builder.append(' '); - builder.append(apkPath); - builder.append(' '); // TODO: Extend getSizeInfo to look at the full subdirectory tree, // not just the first level. - builder.append(libDirPath != null ? libDirPath : "!"); - builder.append(' '); - builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); - builder.append(' '); - builder.append(asecPath != null ? asecPath : "!"); - builder.append(' '); // TODO: Extend getSizeInfo to look at *all* instrution sets, not // just the primary. - builder.append(instructionSets[0]); - - String s = mInstaller.transact(builder.toString()); - String res[] = s.split(" "); + final String rawRes = mInstaller.executeForResult("get_app_size", uuid, pkgname, userid, + flags, apkPath, libDirPath, fwdLockApkPath, asecPath, instructionSets[0]); + final String res[] = rawRes.split(" "); if ((res == null) || (res.length != 5)) { - return -1; + throw new InstallerException("Invalid size result: " + rawRes); } try { pStats.codeSize = Long.parseLong(res[1]); pStats.dataSize = Long.parseLong(res[2]); pStats.cacheSize = Long.parseLong(res[3]); pStats.externalCodeSize = Long.parseLong(res[4]); - return Integer.parseInt(res[0]); } catch (NumberFormatException e) { - return -1; + throw new InstallerException("Invalid size result: " + rawRes); } } - public int moveFiles() { - return mInstaller.execute("movefiles"); + public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded, + int dexFlags) throws InstallerException { + assertValidInstructionSet(instructionSet); + mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags); } - @Deprecated - public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) { - return linkNativeLibraryDirectory(null, dataPath, nativeLibPath32, userId); + public void dexopt(String apkPath, int uid, String pkgName, String instructionSet, + int dexoptNeeded, @Nullable String outputPath, int dexFlags) + throws InstallerException { + assertValidInstructionSet(instructionSet); + mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, + outputPath, dexFlags); } - /** - * Links the 32 bit native library directory in an application's data directory to the - * real location for backward compatibility. Note that no such symlink is created for - * 64 bit shared libraries. - * - * @return -1 on error - */ - public int linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32, - int userId) { - if (dataPath == null) { - Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); - return -1; - } else if (nativeLibPath32 == null) { - Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); - return -1; - } + public void idmap(String targetApkPath, String overlayApkPath, int uid) + throws InstallerException { + mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid); + } - StringBuilder builder = new StringBuilder("linklib"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(dataPath); - builder.append(' '); - builder.append(nativeLibPath32); - builder.append(' '); - builder.append(userId); - - return mInstaller.execute(builder.toString()); + public void rmdex(String codePath, String instructionSet) throws InstallerException { + assertValidInstructionSet(instructionSet); + mInstaller.execute("rmdex", codePath, instructionSet); } - @Deprecated - public boolean restoreconData(String pkgName, String seinfo, int uid) { - return restoreconData(null, pkgName, seinfo, uid); + public void rmPackageDir(String packageDir) throws InstallerException { + mInstaller.execute("rmpackagedir", packageDir); } - public boolean restoreconData(String uuid, String pkgName, String seinfo, int uid) { - StringBuilder builder = new StringBuilder("restorecondata"); - builder.append(' '); - builder.append(escapeNull(uuid)); - builder.append(' '); - builder.append(pkgName); - builder.append(' '); - builder.append(seinfo != null ? seinfo : "!"); - builder.append(' '); - builder.append(uid); - return (mInstaller.execute(builder.toString()) == 0); + public void createUserConfig(int userid) throws InstallerException { + mInstaller.execute("mkuserconfig", userid); } - public int createOatDir(String oatDir, String dexInstructionSet) { - StringBuilder builder = new StringBuilder("createoatdir"); - builder.append(' '); - builder.append(oatDir); - builder.append(' '); - builder.append(dexInstructionSet); - return mInstaller.execute(builder.toString()); + public void removeUserDataDirs(String uuid, int userid) throws InstallerException { + mInstaller.execute("rmuser", uuid, userid); } + public void markBootComplete(String instructionSet) throws InstallerException { + assertValidInstructionSet(instructionSet); + mInstaller.execute("markbootcomplete", instructionSet); + } + + public void freeCache(String uuid, long freeStorageSize) throws InstallerException { + mInstaller.execute("freecache", uuid, freeStorageSize); + } - public int linkFile(String relativePath, String fromBase, String toBase) { - StringBuilder builder = new StringBuilder("linkfile"); - builder.append(' '); - builder.append(relativePath); - builder.append(' '); - builder.append(fromBase); - builder.append(' '); - builder.append(toBase); - return mInstaller.execute(builder.toString()); + public void moveFiles() throws InstallerException { + mInstaller.execute("movefiles"); } /** - * Returns true iff. {@code instructionSet} is a valid instruction set. + * Links the 32 bit native library directory in an application's data + * directory to the real location for backward compatibility. Note that no + * such symlink is created for 64 bit shared libraries. */ - private static boolean isValidInstructionSet(String instructionSet) { - if (instructionSet == null) { - return false; - } + public void linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32, + int userId) throws InstallerException { + mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId); + } + public void createOatDir(String oatDir, String dexInstructionSet) + throws InstallerException { + mInstaller.execute("createoatdir", oatDir, dexInstructionSet); + } + + public void linkFile(String relativePath, String fromBase, String toBase) + throws InstallerException { + mInstaller.execute("linkfile", relativePath, fromBase, toBase); + } + + private static void assertValidInstructionSet(String instructionSet) + throws InstallerException { for (String abi : Build.SUPPORTED_ABIS) { - if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) { - return true; + if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) { + return; } } - - return false; + throw new InstallerException("Invalid instruction set: " + instructionSet); } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index d29a6231c2f5..b45a922460d3 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -27,6 +27,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.os.InstallerConnection.InstallerException; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -166,12 +168,13 @@ final class PackageDexOptimizer { | (vmSafeMode ? DEXOPT_SAFEMODE : 0) | (debuggable ? DEXOPT_DEBUGGABLE : 0) | DEXOPT_BOOTCOMPLETE; - final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid, - pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags); - - // Dex2oat might fail due to compiler / verifier errors. - if (ret == 0) { + try { + mPackageManagerService.mInstaller.dexopt(path, sharedGid, + pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, + dexFlags); performedDexOpt = true; + } catch (InstallerException e) { + Slog.w(TAG, "Failed to dexopt", e); } } } @@ -210,8 +213,13 @@ final class PackageDexOptimizer { File codePath = new File(pkg.codePath); if (codePath.isDirectory()) { File oatDir = getOatDir(codePath); - mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(), - dexInstructionSet); + try { + mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(), + dexInstructionSet); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to create oat dir", e); + return null; + } return oatDir.getAbsolutePath(); } return null; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 55b8bf2f6206..23a58d06eed1 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -258,11 +258,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { for (File stage : unclaimedStages) { Slog.w(TAG, "Deleting orphan stage " + stage); synchronized (mPm.mInstallLock) { - if (stage.isDirectory()) { - mPm.mInstaller.rmPackageDir(stage.getAbsolutePath()); - } else { - stage.delete(); - } + mPm.removeCodePathLI(stage); } } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 1655cb67979d..b84ffa39a803 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -69,6 +69,7 @@ import libcore.io.Libcore; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; +import com.android.internal.os.InstallerConnection.InstallerException; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -832,9 +833,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IOException("File: " + pathStr + " outside base: " + baseStr); } - private void createOatDirs(List<String> instructionSets, File fromDir) { + private void createOatDirs(List<String> instructionSets, File fromDir) + throws PackageManagerException { for (String instructionSet : instructionSets) { - mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); + try { + mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet); + } catch (InstallerException e) { + throw PackageManagerException.from(e); + } } } @@ -842,13 +848,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws IOException { for (File fromFile : fromFiles) { final String relativePath = getRelativePath(fromFile, fromDir); - final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), - toDir.getAbsolutePath()); - - if (ret < 0) { - // installd will log failure details. + try { + mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(), + toDir.getAbsolutePath()); + } catch (InstallerException e) { throw new IOException("failed linkOrCreateDir(" + relativePath + ", " - + fromDir + ", " + toDir + ")"); + + fromDir + ", " + toDir + ")", e); } } @@ -1041,7 +1046,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } if (stageDir != null) { - mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); + try { + mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); + } catch (InstallerException ignored) { + } } if (stageCid != null) { PackageHelper.destroySdDir(stageCid); diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java index a41636e5d1b5..d04eedc68126 100644 --- a/services/core/java/com/android/server/pm/PackageManagerException.java +++ b/services/core/java/com/android/server/pm/PackageManagerException.java @@ -16,8 +16,11 @@ package com.android.server.pm; +import android.content.pm.PackageManager; import android.content.pm.PackageParser.PackageParserException; +import com.android.internal.os.InstallerConnection.InstallerException; + /** {@hide} */ public class PackageManagerException extends Exception { public final int error; @@ -36,4 +39,10 @@ public class PackageManagerException extends Exception { throws PackageManagerException { throw new PackageManagerException(e.error, e.getMessage(), e.getCause()); } + + public static PackageManagerException from(InstallerException e) + throws PackageManagerException { + throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, + e.getMessage(), e.getCause()); + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 41627fd58f01..f777faf3c6c0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -219,6 +219,7 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.os.IParcelFileDescriptorFactory; +import com.android.internal.os.InstallerConnection.InstallerException; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; @@ -2067,7 +2068,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } catch (FileNotFoundException e) { Slog.w(TAG, "Library not found: " + lib); - } catch (IOException e) { + } catch (IOException | InstallerException e) { Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? " + e.getMessage()); } @@ -2136,7 +2137,11 @@ public class PackageManagerService extends IPackageManager.Stub { | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0); if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands"); - mInstaller.moveFiles(); + try { + mInstaller.moveFiles(); + } catch (InstallerException e) { + logCriticalInfo(Log.WARN, "Update commands failed: " + e); + } // Prune any system packages that no longer exist. final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>(); @@ -2710,11 +2715,7 @@ public class PackageManagerService extends IPackageManager.Stub { removeDataDirsLI(ps.volumeUuid, ps.name); if (ps.codePath != null) { - if (ps.codePath.isDirectory()) { - mInstaller.rmPackageDir(ps.codePath.getAbsolutePath()); - } else { - ps.codePath.delete(); - } + removeCodePathLI(ps.codePath); } if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) { if (ps.resourcePath.isDirectory()) { @@ -3038,16 +3039,18 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - int retCode = -1; + boolean success = true; synchronized (mInstallLock) { - retCode = mInstaller.freeCache(volumeUuid, freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + try { + mInstaller.freeCache(volumeUuid, freeStorageSize); + } catch (InstallerException e) { + Slog.w(TAG, "Couldn't clear application caches: " + e); + success = false; } } if (observer != null) { try { - observer.onRemoveCompleted(null, (retCode >= 0)); + observer.onRemoveCompleted(null, success); } catch (RemoteException e) { Slog.w(TAG, "RemoveException when invoking call back"); } @@ -3065,17 +3068,19 @@ public class PackageManagerService extends IPackageManager.Stub { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - int retCode = -1; + boolean success = true; synchronized (mInstallLock) { - retCode = mInstaller.freeCache(volumeUuid, freeStorageSize); - if (retCode < 0) { - Slog.w(TAG, "Couldn't clear application caches"); + try { + mInstaller.freeCache(volumeUuid, freeStorageSize); + } catch (InstallerException e) { + Slog.w(TAG, "Couldn't clear application caches: " + e); + success = false; } } if(pi != null) { try { // Callback via pending intent - int code = (retCode >= 0) ? 1 : 0; + int code = success ? 1 : 0; pi.sendIntent(null, code, null, null, null); } catch (SendIntentException e1) { @@ -3088,8 +3093,10 @@ public class PackageManagerService extends IPackageManager.Stub { void freeStorage(String volumeUuid, long freeStorageSize) throws IOException { synchronized (mInstallLock) { - if (mInstaller.freeCache(volumeUuid, freeStorageSize) < 0) { - throw new IOException("Failed to free enough space"); + try { + mInstaller.freeCache(volumeUuid, freeStorageSize); + } catch (InstallerException e) { + throw new IOException("Failed to free enough space", e); } } } @@ -3121,7 +3128,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** * Update given flags based on encryption status of current user. */ - private int updateFlagsForEncryption(int flags, int userId) { + private int updateFlags(int flags, int userId) { if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) { // Caller expressed an explicit opinion about what encryption @@ -3135,6 +3142,12 @@ public class PackageManagerService extends IPackageManager.Stub { flags |= PackageManager.MATCH_ENCRYPTION_AWARE; } } + + // Safe mode means we should ignore any third-party apps + if (mSafeMode) { + flags |= PackageManager.MATCH_SYSTEM_ONLY; + } + return flags; } @@ -3162,7 +3175,7 @@ public class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie + " with flags 0x" + Integer.toHexString(flags), new Throwable()); } - return updateFlagsForEncryption(flags, userId); + return updateFlags(flags, userId); } /** @@ -3194,7 +3207,7 @@ public class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie + " with flags 0x" + Integer.toHexString(flags), new Throwable()); } - return updateFlagsForEncryption(flags, userId); + return updateFlags(flags, userId); } /** @@ -5941,8 +5954,6 @@ public class PackageManagerService extends IPackageManager.Stub { : null; return ps != null && mSettings.isEnabledAndMatchLPr(provider.info, flags, userId) - && (!mSafeMode || (provider.info.applicationInfo.flags - &ApplicationInfo.FLAG_SYSTEM) != 0) ? PackageParser.generateProviderInfo(provider, flags, ps.readUserState(userId), userId) : null; @@ -5997,9 +6008,7 @@ public class PackageManagerService extends IPackageManager.Stub { && (processName == null || (p.info.processName.equals(processName) && UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) - && mSettings.isEnabledAndMatchLPr(p.info, flags, userId) - && (!mSafeMode - || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) { + && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) { if (finalList == null) { finalList = new ArrayList<ProviderInfo>(3); } @@ -6084,7 +6093,9 @@ public class PackageManagerService extends IPackageManager.Stub { } final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); // TODO: generate idmap for split APKs - if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) { + try { + mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid); + } catch (InstallerException e) { Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and " + opkg.baseCodePath); return false; @@ -6144,11 +6155,7 @@ public class PackageManagerService extends IPackageManager.Stub { if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 && e.error == PackageManager.INSTALL_FAILED_INVALID_APK) { logCriticalInfo(Log.WARN, "Deleting invalid package at " + file); - if (file.isDirectory()) { - mInstaller.rmPackageDir(file.getAbsolutePath()); - } else { - file.delete(); - } + removeCodePathLI(file); } } } @@ -6714,48 +6721,63 @@ public class PackageManagerService extends IPackageManager.Stub { private void createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) throws PackageManagerException { - int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo); - if (res != 0) { + // TODO: triage flags as part of 26466827 + final int appId = UserHandle.getAppId(uid); + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + + try { + final int[] users = sUserManager.getUserIds(); + for (int user : users) { + mInstaller.createAppData(volumeUuid, packageName, user, flags, appId, seinfo); + } + } catch (InstallerException e) { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, - "Failed to install " + packageName + ": " + res); + "Failed to prepare data directory", e); } + } + private boolean removeDataDirsLI(String volumeUuid, String packageName) { + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + + boolean res = true; final int[] users = sUserManager.getUserIds(); for (int user : users) { - if (user != 0) { - res = mInstaller.createUserData(volumeUuid, packageName, - UserHandle.getUid(user, uid), user, seinfo); - if (res != 0) { - throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, - "Failed to createUserData " + packageName + ": " + res); - } + try { + mInstaller.destroyAppData(volumeUuid, packageName, user, flags); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to delete data directory", e); + res = false; } } + return res; } - private int removeDataDirsLI(String volumeUuid, String packageName) { - int[] users = sUserManager.getUserIds(); - int res = 0; - for (int user : users) { - int resInner = mInstaller.remove(volumeUuid, packageName, user); - if (resInner < 0) { - res = resInner; + void removeCodePathLI(File codePath) { + if (codePath.isDirectory()) { + try { + mInstaller.rmPackageDir(codePath.getAbsolutePath()); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to remove code path", e); } + } else { + codePath.delete(); } - - return res; } - private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) { - int[] users = sUserManager.getUserIds(); - int res = 0; + private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) { + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + + final int[] users = sUserManager.getUserIds(); for (int user : users) { - int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user); - if (resInner < 0) { - res = resInner; + try { + mInstaller.clearAppData(volumeUuid, packageName, user, + flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to delete code cache directory", e); } } - return res; } private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file, @@ -7256,6 +7278,8 @@ public class PackageManagerService extends IPackageManager.Stub { final File dataPath = Environment.getDataUserCredentialEncryptedPackageDirectory( pkg.volumeUuid, UserHandle.USER_SYSTEM, pkg.packageName); + // TOOD: switch to ensure various directories + boolean uidError = false; if (dataPath.exists()) { int currentUid = 0; @@ -7269,27 +7293,12 @@ public class PackageManagerService extends IPackageManager.Stub { // If we have mismatched owners for the data path, we have a problem. if (currentUid != pkg.applicationInfo.uid) { boolean recovered = false; - if (currentUid == 0) { - // The directory somehow became owned by root. Wow. - // This is probably because the system was stopped while - // installd was in the middle of messing with its libs - // directory. Ask installd to fix that. - int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName, - pkg.applicationInfo.uid, pkg.applicationInfo.uid); - if (ret >= 0) { - recovered = true; - String msg = "Package " + pkg.packageName - + " unexpectedly changed to uid 0; recovered to " + - + pkg.applicationInfo.uid; - reportSettingsProblem(Log.WARN, msg); - } - } - if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 - || (scanFlags&SCAN_BOOTING) != 0)) { + if (((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0 + || (scanFlags & SCAN_BOOTING) != 0)) { // If this is a system app, we can at least delete its // current data so the application will still work. - int ret = removeDataDirsLI(pkg.volumeUuid, pkgName); - if (ret >= 0) { + boolean res = removeDataDirsLI(pkg.volumeUuid, pkgName); + if (res) { // TODO: Kill the processes first // Old data gone! String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0 @@ -7304,11 +7313,12 @@ public class PackageManagerService extends IPackageManager.Stub { if (!recovered) { mHasSystemUidErrors = true; } - } else if (!recovered) { + } else { // If we allow this install to proceed, we will be broken. // Abort, abort! throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED, - "scanPackageLI"); + "Expected data to be owned by UID " + pkg.applicationInfo.uid + + " but found " + currentUid); } if (!recovered) { pkg.applicationInfo.dataDir = "/mismatched_uid/settings_" @@ -7338,8 +7348,16 @@ public class PackageManagerService extends IPackageManager.Stub { if (mShouldRestoreconData) { Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued."); - mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName, - pkg.applicationInfo.seinfo, pkg.applicationInfo.uid); + // TODO: extend this to restorecon over all users + final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + mInstaller.restoreconAppData(pkg.volumeUuid, pkg.packageName, + UserHandle.USER_SYSTEM, flags, appId, pkg.applicationInfo.seinfo); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to restorecon " + pkg.packageName, e); + } } } else { if (DEBUG_PACKAGE_SCANNING) { @@ -7395,9 +7413,15 @@ public class PackageManagerService extends IPackageManager.Stub { if (!TextUtils.isEmpty(pkg.volumeUuid)) { for (int userId : userIds) { if (userId != UserHandle.USER_SYSTEM) { - mInstaller.createUserData(pkg.volumeUuid, pkg.packageName, - UserHandle.getUid(userId, pkg.applicationInfo.uid), userId, - pkg.applicationInfo.seinfo); + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); + try { + mInstaller.createAppData(pkg.volumeUuid, pkg.packageName, userId, + flags, appId, pkg.applicationInfo.seinfo); + } catch (InstallerException e) { + throw PackageManagerException.from(e); + } } } } @@ -7411,10 +7435,11 @@ public class PackageManagerService extends IPackageManager.Stub { try { final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir; for (int userId : userIds) { - if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, - nativeLibPath, userId) < 0) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Failed linking native library dir (user=" + userId + ")"); + try { + mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, + nativeLibPath, userId); + } catch (InstallerException e) { + throw PackageManagerException.from(e); } } } finally { @@ -8147,8 +8172,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (ps.pkg != null && ps.pkg.applicationInfo != null) { ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi); - mInstaller.rmdex(ps.codePathString, - getDexCodeInstructionSet(getPreferredInstructionSet())); + try { + mInstaller.rmdex(ps.codePathString, + getDexCodeInstructionSet(getPreferredInstructionSet())); + } catch (InstallerException ignored) { + } } } } @@ -9265,10 +9293,6 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } final PackageParser.Activity activity = info.activity; - if (mSafeMode && (activity.info.applicationInfo.flags - &ApplicationInfo.FLAG_SYSTEM) == 0) { - return null; - } PackageSetting ps = (PackageSetting) activity.owner.mExtras; if (ps == null) { return null; @@ -9489,10 +9513,6 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } final PackageParser.Service service = info.service; - if (mSafeMode && (service.info.applicationInfo.flags - &ApplicationInfo.FLAG_SYSTEM) == 0) { - return null; - } PackageSetting ps = (PackageSetting) service.owner.mExtras; if (ps == null) { return null; @@ -9712,10 +9732,6 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } final PackageParser.Provider provider = info.provider; - if (mSafeMode && (provider.info.applicationInfo.flags - & ApplicationInfo.FLAG_SYSTEM) == 0) { - return null; - } PackageSetting ps = (PackageSetting) provider.owner.mExtras; if (ps == null) { return null; @@ -11143,9 +11159,12 @@ public class PackageManagerService extends IPackageManager.Stub { final long sizeBytes = mContainerService.calculateInstalledSize( origin.resolvedPath, isForwardLocked(), packageAbiOverride); - if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) { + try { + mInstaller.freeCache(null, sizeBytes + lowThreshold); pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to free cache", e); } /* @@ -11543,11 +11562,9 @@ public class PackageManagerService extends IPackageManager.Stub { String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); for (String codePath : allCodePaths) { for (String dexCodeInstructionSet : dexCodeInstructionSets) { - int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove dex file for package at location " + codePath - + ", retcode=" + retCode); - // we don't consider this to be a failure of the core package deletion + try { + mInstaller.rmdex(codePath, dexCodeInstructionSet); + } catch (InstallerException ignored) { } } } @@ -11733,11 +11750,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } - if (codeFile.isDirectory()) { - mInstaller.rmPackageDir(codeFile.getAbsolutePath()); - } else { - codeFile.delete(); - } + removeCodePathLI(codeFile); if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) { resourceFile.delete(); @@ -12114,8 +12127,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from " + move.fromUuid + " to " + move.toUuid); synchronized (mInstaller) { - if (mInstaller.copyCompleteApp(move.fromUuid, move.toUuid, move.packageName, - move.dataAppName, move.appId, move.seinfo) != 0) { + try { + mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName, + move.dataAppName, move.appId, move.seinfo); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to move app", e); return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } } @@ -12178,11 +12194,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mInstallLock) { // Clean up both app data and code removeDataDirsLI(volumeUuid, move.packageName); - if (codeFile.isDirectory()) { - mInstaller.rmPackageDir(codeFile.getAbsolutePath()); - } else { - codeFile.delete(); - } + removeCodePathLI(codeFile); } return true; } @@ -13863,7 +13875,13 @@ public class PackageManagerService extends IPackageManager.Stub { outInfo.removedAppId = appId; outInfo.removedUsers = new int[] {removeUser}; } - mInstaller.clearUserData(ps.volumeUuid, packageName, removeUser); + // TODO: triage flags as part of 26466827 + final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to delete app data", e); + } removeKeystoreDataIfNeeded(removeUser, appId); schedulePackageCleaning(packageName, removeUser, false); synchronized (mPackages) { @@ -14037,13 +14055,16 @@ public class PackageManagerService extends IPackageManager.Stub { // Always delete data directories for package, even if we found no other // record of app. This helps users recover from UID mismatches without // resorting to a full data wipe. - int retCode = mInstaller.clearUserData(pkg.volumeUuid, packageName, userId); - if (retCode < 0) { - Slog.w(TAG, "Couldn't remove cache files for package " + packageName); + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags); + } catch (InstallerException e) { + Slog.w(TAG, "Couldn't remove cache files for package " + packageName, e); return false; } - final int appId = pkg.applicationInfo.uid; + final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); removeKeystoreDataIfNeeded(userId, appId); // Create a native library symlink only if we have native libraries @@ -14052,9 +14073,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkg.applicationInfo.primaryCpuAbi != null && !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) { final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir; - if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, - nativeLibPath, userId) < 0) { - Slog.w(TAG, "Failed linking native library dir"); + try { + mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName, + nativeLibPath, userId); + } catch (InstallerException e) { + Slog.w(TAG, "Failed linking native library dir", e); return false; } } @@ -14267,10 +14290,14 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Package " + packageName + " has no applicationInfo."); return false; } - int retCode = mInstaller.deleteCacheFiles(p.volumeUuid, packageName, userId); - if (retCode < 0) { + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + mInstaller.clearAppData(p.volumeUuid, packageName, userId, + flags | Installer.FLAG_CLEAR_CACHE_ONLY); + } catch (InstallerException e) { Slog.w(TAG, "Couldn't remove cache files for package " - + packageName + " u" + userId); + + packageName + " u" + userId, e); return false; } return true; @@ -14364,9 +14391,12 @@ public class PackageManagerService extends IPackageManager.Stub { apkPath = p.baseCodePath; } - int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath, - libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); - if (res < 0) { + // TODO: triage flags as part of 26466827 + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath, + libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); + } catch (InstallerException e) { return false; } @@ -16581,7 +16611,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (destroyUser) { synchronized (mInstallLock) { - mInstaller.removeUserDataDirs(volumeUuid, userId); + try { + mInstaller.removeUserDataDirs(volumeUuid, userId); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to clean up user dirs", e); + } } } } @@ -16647,11 +16681,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (packageName != null) { removeDataDirsLI(volumeUuid, packageName); } - if (file.isDirectory()) { - mInstaller.rmPackageDir(file.getAbsolutePath()); - } else { - file.delete(); - } + removeCodePathLI(file); } } } @@ -16987,7 +17017,11 @@ public class PackageManagerService extends IPackageManager.Stub { for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { final String volumeUuid = vol.getFsUuid(); if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid); - mInstaller.removeUserDataDirs(volumeUuid, userHandle); + try { + mInstaller.removeUserDataDirs(volumeUuid, userHandle); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to remove user data", e); + } } synchronized (mPackages) { removeUnusedPackagesLILPw(userManager, userHandle); @@ -17050,7 +17084,11 @@ public class PackageManagerService extends IPackageManager.Stub { /** Called by UserManagerService */ void createNewUser(int userHandle) { synchronized (mInstallLock) { - mInstaller.createUserConfig(userHandle); + try { + mInstaller.createUserConfig(userHandle); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to create user config", e); + } mSettings.createNewUserLI(this, mInstaller, userHandle); } synchronized (mPackages) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3f9ce7a64aae..9fef51565c04 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -81,6 +81,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; +import com.android.internal.os.InstallerConnection.InstallerException; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; @@ -3668,7 +3669,7 @@ final class Settings { int userHandle) { String[] volumeUuids; String[] names; - int[] uids; + int[] appIds; String[] seinfos; int packagesCount; synchronized (mPackages) { @@ -3676,7 +3677,7 @@ final class Settings { packagesCount = packages.size(); volumeUuids = new String[packagesCount]; names = new String[packagesCount]; - uids = new int[packagesCount]; + appIds = new int[packagesCount]; seinfos = new String[packagesCount]; Iterator<PackageSetting> packagesIterator = packages.iterator(); for (int i = 0; i < packagesCount; i++) { @@ -3690,7 +3691,7 @@ final class Settings { // required args and call the installer after mPackages lock has been released volumeUuids[i] = ps.volumeUuid; names[i] = ps.name; - uids[i] = UserHandle.getUid(userHandle, ps.appId); + appIds[i] = ps.appId; seinfos[i] = ps.pkg.applicationInfo.seinfo; } } @@ -3698,7 +3699,14 @@ final class Settings { if (names[i] == null) { continue; } - installer.createUserData(volumeUuids[i], names[i], uids[i], userHandle, seinfos[i]); + // TODO: triage flags! + final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE; + try { + installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i], + seinfos[i]); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to prepare app data", e); + } } synchronized (mPackages) { applyDefaultPreferredAppsLPw(service, userHandle); @@ -3799,63 +3807,11 @@ final class Settings { } boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) { - return isEnabledLPr(componentInfo, flags, userId) - && isMatchLPr(componentInfo, flags); - } - - private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) { - if ((flags & MATCH_DISABLED_COMPONENTS) != 0) { - return true; - } - final PackageSetting packageSettings = mPackages.get(componentInfo.packageName); - if (PackageManagerService.DEBUG_SETTINGS) { - Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = " - + componentInfo.packageName + " componentName = " + componentInfo.name); - Log.v(PackageManagerService.TAG, "enabledComponents: " - + compToString(packageSettings.getEnabledComponents(userId))); - Log.v(PackageManagerService.TAG, "disabledComponents: " - + compToString(packageSettings.getDisabledComponents(userId))); - } - if (packageSettings == null) { - return false; - } - PackageUserState ustate = packageSettings.readUserState(userId); - if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) { - if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { - return true; - } - } - if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED - || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER - || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED - || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled - && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) { - return false; - } - if (ustate.enabledComponents != null - && ustate.enabledComponents.contains(componentInfo.name)) { - return true; - } - if (ustate.disabledComponents != null - && ustate.disabledComponents.contains(componentInfo.name)) { - return false; - } - return componentInfo.enabled; - } - - private boolean isMatchLPr(ComponentInfo componentInfo, int flags) { - if ((flags & MATCH_SYSTEM_ONLY) != 0) { - final PackageSetting ps = mPackages.get(componentInfo.packageName); - if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { - return false; - } - } + final PackageSetting ps = mPackages.get(componentInfo.packageName); + if (ps == null) return false; - final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0) - && !componentInfo.encryptionAware; - final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0) - && componentInfo.encryptionAware; - return matchesUnaware || matchesAware; + final PackageUserState userState = ps.readUserState(userId); + return userState.isMatch(componentInfo, flags); } String getInstallerPackageNameLPr(String packageName) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index de1c1ea091df..75910a76cffb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -6402,6 +6402,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mLockScreenTimerActive != enable) { if (enable) { if (localLOGV) Log.v(TAG, "setting lockscreen timer"); + mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout); } else { if (localLOGV) Log.v(TAG, "clearing lockscreen timer"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index dd58b3c4443e..380763abf6e3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -388,6 +388,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features"; private static final String TAG_DISABLE_CAMERA = "disable-camera"; private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id"; + private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search"; private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING = "disable-bt-contacts-sharing"; private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture"; @@ -476,6 +477,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean encryptionRequested = false; boolean disableCamera = false; boolean disableCallerId = false; + boolean disableContactsSearch = false; boolean disableBluetoothContactSharing = true; boolean disableScreenCapture = false; // Can only be set by a device/profile owner. boolean requireAutoTime = false; // Can only be set by a device owner. @@ -638,6 +640,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId)); out.endTag(null, TAG_DISABLE_CALLER_ID); } + if (disableContactsSearch) { + out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH); + out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch)); + out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH); + } if (disableBluetoothContactSharing) { out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING); out.attribute(null, ATTR_VALUE, @@ -809,6 +816,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if (TAG_DISABLE_CALLER_ID.equals(tag)) { disableCallerId = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) { + disableContactsSearch = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) { disableBluetoothContactSharing = Boolean.parseBoolean(parser .getAttributeValue(null, ATTR_VALUE)); @@ -1034,6 +1044,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(disableCamera); pw.print(prefix); pw.print("disableCallerId="); pw.println(disableCallerId); + pw.print(prefix); pw.print("disableContactsSearch="); + pw.println(disableContactsSearch); pw.print(prefix); pw.print("disableBluetoothContactSharing="); pw.println(disableBluetoothContactSharing); pw.print(prefix); pw.print("disableScreenCapture="); @@ -1808,7 +1820,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return null; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); Intent resolveIntent = new Intent(); resolveIntent.setComponent(adminName); List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceiversAsUser( @@ -2411,7 +2423,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Bundle onEnableData) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); DevicePolicyData policy = getUserData(userHandle); DeviceAdminInfo info = findAdmin(adminReceiver, userHandle, @@ -2457,7 +2469,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null; } @@ -2468,7 +2480,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { DevicePolicyData policyData = getUserData(userHandle); return policyData.mRemovingAdmins.contains(adminReceiver); @@ -2480,7 +2492,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (administrator == null) { @@ -2497,7 +2509,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return Collections.EMPTY_LIST; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); @@ -2517,7 +2529,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); @@ -2535,7 +2547,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin == null) { @@ -2594,7 +2606,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; @@ -2664,7 +2676,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -2711,7 +2723,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -2771,7 +2783,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0L; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { long timeout = 0L; @@ -2898,7 +2910,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0L; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { return getPasswordExpirationLocked(who, userHandle); } @@ -2926,7 +2938,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -2970,7 +2982,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -3017,7 +3029,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -3067,7 +3079,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -3117,7 +3129,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -3167,7 +3179,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int length = 0; @@ -3200,7 +3212,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return true; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { int id = getCredentialOwner(userHandle); @@ -3272,7 +3284,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle) : getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle); @@ -3285,7 +3297,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return UserHandle.USER_NULL; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle); return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL; @@ -3584,7 +3596,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { long time = 0; @@ -3906,7 +3918,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } final int userHandle = mInjector.userHandleGetCallingUserId(); - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { // This API can only be called by an active device admin, // so try to retrieve it to check that the caller is one. @@ -3985,7 +3997,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); @@ -4014,7 +4026,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); // Managed Profile password can only be changed when per user encryption is present. if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) { enforceNotManagedProfile(userHandle, "set the active password"); @@ -4083,7 +4095,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void reportFailedPasswordAttempt(int userHandle) { - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); enforceNotManagedProfile(userHandle, "report failed password attempt"); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); @@ -4125,7 +4137,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public void reportSuccessfulPasswordAttempt(int userHandle) { - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); @@ -4209,7 +4221,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return null; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized(this) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); // Scan through active admins and find if anyone has already @@ -4346,7 +4358,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return false; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { // Check for permissions if a particular caller is specified if (who != null) { @@ -4376,7 +4388,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { // Ok to return current status. } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); return getEncryptionStatus(); } @@ -4624,7 +4636,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return 0; } - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); long ident = mInjector.binderClearCallingIdentity(); try { synchronized (this) { @@ -5185,17 +5197,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void enforceCrossUserPermission(int userHandle) { + private void enforceFullCrossUsersPermission(int userHandle) { + enforceSystemUserOrPermission(userHandle, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + } + + private void enforceCrossUsersPermission(int userHandle) { + enforceSystemUserOrPermission(userHandle, + android.Manifest.permission.INTERACT_ACROSS_USERS); + } + + private void enforceSystemUserOrPermission(int userHandle, String permission) { if (userHandle < 0) { throw new IllegalArgumentException("Invalid userId " + userHandle); } final int callingUid = mInjector.binderGetCallingUid(); - if (userHandle == UserHandle.getUserId(callingUid)) return; + if (userHandle == UserHandle.getUserId(callingUid)) { + return; + } if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == Process.ROOT_UID)) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have" - + " INTERACT_ACROSS_USERS_FULL permission"); + mContext.enforceCallingOrSelfPermission(permission, + "Must be system or have " + permission + " permission"); } } @@ -5405,7 +5428,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } Preconditions.checkNotNull(agent, "agent null"); - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { final String componentName = agent.flattenToString(); @@ -6068,7 +6091,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public Bundle getUserRestrictions(ComponentName who, int userHandle) { Preconditions.checkNotNull(who, "ComponentName is null"); - enforceCrossUserPermission(userHandle); + enforceFullCrossUsersPermission(userHandle); synchronized (this) { ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle); if (activeAdmin == null) { @@ -6260,7 +6283,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public String[] getAccountTypesWithManagementDisabledAsUser(int userId) { - enforceCrossUserPermission(userId); + enforceFullCrossUsersPermission(userId); if (!mHasFeature) { return null; } @@ -6332,7 +6355,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.disableCallerId != disabled) { admin.disableCallerId = disabled; - saveSettingsLocked(UserHandle.getCallingUserId()); + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } } } @@ -6352,8 +6375,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean getCrossProfileCallerIdDisabledForUser(int userId) { - // TODO: Should there be a check to make sure this relationship is within a profile group? - //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system"); + enforceCrossUsersPermission(userId); synchronized (this) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableCallerId : false; @@ -6361,6 +6383,44 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) { + if (!mHasFeature) { + return; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + synchronized (this) { + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (admin.disableContactsSearch != disabled) { + admin.disableContactsSearch = disabled; + saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + } + } + } + + @Override + public boolean getCrossProfileContactsSearchDisabled(ComponentName who) { + if (!mHasFeature) { + return false; + } + Preconditions.checkNotNull(who, "ComponentName is null"); + synchronized (this) { + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + return admin.disableContactsSearch; + } + } + + @Override + public boolean getCrossProfileContactsSearchDisabledForUser(int userId) { + enforceCrossUsersPermission(userId); + synchronized (this) { + ActiveAdmin admin = getProfileOwnerAdminLocked(userId); + return (admin != null) ? admin.disableContactsSearch : false; + } + } + + @Override public void startManagedQuickContact(String actualLookupKey, long actualContactId, long actualDirectoryId, Intent originalIntent) { final Intent intent = QuickContact.rebuildManagedQuickContactsIntent( diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java index 2e0866cc130a..5abb6e7d9367 100644 --- a/services/print/java/com/android/server/print/PrintManagerService.java +++ b/services/print/java/com/android/server/print/PrintManagerService.java @@ -555,8 +555,14 @@ public final class PrintManagerService extends SystemService { // to handle it as the change may affect ongoing print jobs. UserState userState = getOrCreateUserStateLocked(getChangingUserId()); boolean stoppedSomePackages = false; - Iterator<PrintServiceInfo> iterator = userState.getEnabledPrintServices() - .iterator(); + + List<PrintServiceInfo> enabledServices = userState + .getEnabledPrintServices(); + if (enabledServices == null) { + return false; + } + + Iterator<PrintServiceInfo> iterator = enabledServices.iterator(); while (iterator.hasNext()) { ComponentName componentName = iterator.next().getComponentName(); String componentPackage = componentName.getPackageName(); diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 41982170c84c..78edc4df08fa 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_META_DATA; import static android.content.pm.PackageManager.GET_SERVICES; import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -336,7 +337,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null); } - public List<PrintServiceInfo> getEnabledPrintServices() { + public @Nullable List<PrintServiceInfo> getEnabledPrintServices() { synchronized (mLock) { List<PrintServiceInfo> enabledServices = null; final int installedServiceCount = mInstalledServices.size(); diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 27deb720c4b7..27d52071658f 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -59,6 +59,7 @@ import android.os.MessageQueue; import android.os.MessageQueue.IdleHandler; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.LogPrinter; @@ -1504,4 +1505,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { ka3.stop(); callback3.expectStopped(); } + + @SmallTest + public void testGetCaptivePortalServerUrl() throws Exception { + String url = mCm.getCaptivePortalServerUrl(); + assertEquals("http://connectivitycheck.gstatic.com/generate_204", url); + } } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 72621a422ce6..630367de40a9 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -59,6 +59,7 @@ class UserUsageStatsService { private final Context mContext; private final UsageStatsDatabase mDatabase; private final IntervalStats[] mCurrentStats; + private IntervalStats mAppIdleRollingWindow; private boolean mStatsChanged = false; private final UnixCalendar mDailyExpiryDate; private final StatsUpdatedListener mListener; @@ -137,6 +138,8 @@ class UserUsageStatsService { initializeDefaultsForApps(currentTimeMillis, deviceUsageTime, mDatabase.isFirstUpdate()); } + + refreshAppIdleRollingWindow(currentTimeMillis); } /** @@ -170,6 +173,7 @@ class UserUsageStatsService { persistActiveStats(); mDatabase.onTimeChanged(newTime - oldTime); loadActiveStats(newTime, resetBeginIdleTime); + refreshAppIdleRollingWindow(newTime); } void reportEvent(UsageEvents.Event event, long deviceUsageTime) { @@ -211,6 +215,11 @@ class UserUsageStatsService { } } + if (event.mEventType != Event.CONFIGURATION_CHANGE) { + mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType); + mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime); + } + notifyStatsChanged(); } @@ -222,6 +231,7 @@ class UserUsageStatsService { for (IntervalStats stats : mCurrentStats) { stats.updateBeginIdleTime(packageName, beginIdleTime); } + mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime); notifyStatsChanged(); } @@ -229,6 +239,7 @@ class UserUsageStatsService { for (IntervalStats stats : mCurrentStats) { stats.updateSystemLastUsedTime(packageName, lastUsedTime); } + mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime); notifyStatsChanged(); } @@ -387,9 +398,8 @@ class UserUsageStatsService { } long getBeginIdleTime(String packageName) { - final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]; UsageStats packageUsage; - if ((packageUsage = yearly.packageStats.get(packageName)) == null) { + if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) { return -1; } else { return packageUsage.getBeginIdleTime(); @@ -397,9 +407,8 @@ class UserUsageStatsService { } long getSystemLastUsedTime(String packageName) { - final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]; UsageStats packageUsage; - if ((packageUsage = yearly.packageStats.get(packageName)) == null) { + if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) { return -1; } else { return packageUsage.getLastTimeSystemUsed(); @@ -461,6 +470,8 @@ class UserUsageStatsService { } persistActiveStats(); + refreshAppIdleRollingWindow(currentTimeMillis); + final long totalTime = SystemClock.elapsedRealtime() - startTime; Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime + " milliseconds"); @@ -503,6 +514,7 @@ class UserUsageStatsService { } } } + mStatsChanged = false; updateRolloverDeadline(); } @@ -516,6 +528,68 @@ class UserUsageStatsService { mDailyExpiryDate.getTimeInMillis() + ")"); } + private static void mergePackageStats(IntervalStats dst, IntervalStats src) { + dst.endTime = Math.max(dst.endTime, src.endTime); + + final int srcPackageCount = src.packageStats.size(); + for (int i = 0; i < srcPackageCount; i++) { + final String packageName = src.packageStats.keyAt(i); + final UsageStats srcStats = src.packageStats.valueAt(i); + final UsageStats dstStats = dst.packageStats.get(packageName); + if (dstStats == null) { + dst.packageStats.put(packageName, new UsageStats(srcStats)); + } else { + dstStats.add(src.packageStats.valueAt(i)); + } + } + } + + /** + * Merges all the stats into the first element of the resulting list. + */ + private static final StatCombiner<IntervalStats> sPackageStatsMerger = + new StatCombiner<IntervalStats>() { + @Override + public void combine(IntervalStats stats, boolean mutable, + List<IntervalStats> accumulatedResult) { + IntervalStats accum; + if (accumulatedResult.isEmpty()) { + accum = new IntervalStats(); + accum.beginTime = stats.beginTime; + accumulatedResult.add(accum); + } else { + accum = accumulatedResult.get(0); + } + + mergePackageStats(accum, stats); + } + }; + + /** + * App idle operates on a rolling window of time. When we roll over time, we end up with a + * period of time where in-memory stats are empty and we don't hit the disk for older stats + * for performance reasons. Suddenly all apps will become idle. + * + * Instead, at times we do a deep query to find all the apps that have run in the past few + * days and keep the cached data up to date. + * + * @param currentTimeMillis + */ + void refreshAppIdleRollingWindow(long currentTimeMillis) { + // Start the rolling window for AppIdle requests. + List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, + currentTimeMillis - (1000 * 60 * 60 * 24 * 2), currentTimeMillis, + sPackageStatsMerger); + + if (stats == null || stats.isEmpty()) { + mAppIdleRollingWindow = new IntervalStats(); + mergePackageStats(mAppIdleRollingWindow, + mCurrentStats[UsageStatsManager.INTERVAL_YEARLY]); + } else { + mAppIdleRollingWindow = stats.get(0); + } + } + // // -- DUMP related methods -- // @@ -538,6 +612,9 @@ class UserUsageStatsService { pw.println(" stats"); printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true); } + + pw.println("AppIdleRollingWindow cache"); + printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true); } private String formatDateTime(long dateTime, boolean pretty) { diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index b18af33a3a3b..b56ce736aef6 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -687,7 +687,7 @@ public final class PhoneAccount implements Parcelable { .append("] PhoneAccount: ") .append(mAccountHandle) .append(" Capabilities: ") - .append(mCapabilities) + .append(capabilitiesToString(mCapabilities)) .append(" Schemes: "); for (String scheme : mSupportedUriSchemes) { sb.append(scheme) @@ -698,4 +698,42 @@ public final class PhoneAccount implements Parcelable { sb.append("]"); return sb.toString(); } + + /** + * Generates a string representation of a capabilities bitmask. + * + * @param capabilities The capabilities bitmask. + * @return String representation of the capabilities bitmask. + */ + private String capabilitiesToString(int capabilities) { + StringBuilder sb = new StringBuilder(); + if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) { + sb.append("Video "); + } + if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) { + sb.append("Presence "); + } + if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) { + sb.append("CallProvider "); + } + if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) { + sb.append("CallSubject "); + } + if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) { + sb.append("ConnectionMgr "); + } + if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) { + sb.append("EmergOnly "); + } + if (hasCapabilities(CAPABILITY_MULTI_USER)) { + sb.append("MultiUser "); + } + if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) { + sb.append("PlaceEmerg "); + } + if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) { + sb.append("SimSub "); + } + return sb.toString(); + } } diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java index dabf706bc976..216603cad5ee 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -16,13 +16,23 @@ package android.telecom; +import android.annotation.IntDef; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents attributes of video calls. */ public class VideoProfile implements Parcelable { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({QUALITY_UNKNOWN, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_DEFAULT}) + public @interface VideoQuality {} + /** * "Unknown" video quality. * @hide @@ -48,6 +58,14 @@ public class VideoProfile implements Parcelable { */ public static final int QUALITY_DEFAULT = 4; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + flag = true, + value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL, + STATE_PAUSED}) + public @interface VideoState {} + /** * Used when answering or dialing a call to indicate that the call does not have a video * component. @@ -107,7 +125,7 @@ public class VideoProfile implements Parcelable { * * @param videoState The video state. */ - public VideoProfile(int videoState) { + public VideoProfile(@VideoState int videoState) { this(videoState, QUALITY_DEFAULT); } @@ -117,7 +135,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @param quality The video quality. */ - public VideoProfile(int videoState, int quality) { + public VideoProfile(@VideoState int videoState, @VideoQuality int quality) { mVideoState = videoState; mQuality = quality; } @@ -130,6 +148,7 @@ public class VideoProfile implements Parcelable { * {@link VideoProfile#STATE_RX_ENABLED}, * {@link VideoProfile#STATE_PAUSED}. */ + @VideoState public int getVideoState() { return mVideoState; } @@ -139,6 +158,7 @@ public class VideoProfile implements Parcelable { * Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM}, * {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}. */ + @VideoQuality public int getQuality() { return mQuality; } @@ -211,7 +231,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return String representation of the video state. */ - public static String videoStateToString(int videoState) { + public static String videoStateToString(@VideoState int videoState) { StringBuilder sb = new StringBuilder(); sb.append("Audio"); @@ -240,7 +260,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if the video state is audio only, {@code false} otherwise. */ - public static boolean isAudioOnly(int videoState) { + public static boolean isAudioOnly(@VideoState int videoState) { return !hasState(videoState, VideoProfile.STATE_TX_ENABLED) && !hasState(videoState, VideoProfile.STATE_RX_ENABLED); } @@ -251,7 +271,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if video transmission or reception is enabled, {@code false} otherwise. */ - public static boolean isVideo(int videoState) { + public static boolean isVideo(@VideoState int videoState) { return hasState(videoState, VideoProfile.STATE_TX_ENABLED) || hasState(videoState, VideoProfile.STATE_RX_ENABLED) || hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL); @@ -263,7 +283,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if video transmission is enabled, {@code false} otherwise. */ - public static boolean isTransmissionEnabled(int videoState) { + public static boolean isTransmissionEnabled(@VideoState int videoState) { return hasState(videoState, VideoProfile.STATE_TX_ENABLED); } @@ -273,7 +293,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if video reception is enabled, {@code false} otherwise. */ - public static boolean isReceptionEnabled(int videoState) { + public static boolean isReceptionEnabled(@VideoState int videoState) { return hasState(videoState, VideoProfile.STATE_RX_ENABLED); } @@ -283,7 +303,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if the video is bi-directional, {@code false} otherwise. */ - public static boolean isBidirectional(int videoState) { + public static boolean isBidirectional(@VideoState int videoState) { return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL); } @@ -293,7 +313,7 @@ public class VideoProfile implements Parcelable { * @param videoState The video state. * @return {@code True} if the video is paused, {@code false} otherwise. */ - public static boolean isPaused(int videoState) { + public static boolean isPaused(@VideoState int videoState) { return hasState(videoState, VideoProfile.STATE_PAUSED); } @@ -304,7 +324,7 @@ public class VideoProfile implements Parcelable { * @param state The state to check. * @return {@code True} if the state is set. */ - private static boolean hasState(int videoState, int state) { + private static boolean hasState(@VideoState int videoState, @VideoState int state) { return (videoState & state) == state; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 6ffc02614657..80c5b1e204f4 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -473,6 +473,15 @@ public class CarrierConfigManager { public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool"; /** + * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference. + * Some operators support Wi-Fi Calling only, not VoLTE. + * They don't need "Cellular preferred" option. + * In this case, set uneditalbe attribute for preferred preference. + * @hide + */ + public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool"; + + /** * If this is true, the SIM card (through Customer Service Profile EF file) will be able to * prevent manual operator selection. If false, this SIM setting will be ignored and manual * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more @@ -553,6 +562,23 @@ public class CarrierConfigManager { public static final String BOOL_ALLOW_VIDEO_PAUSE = "bool_allow_video_pause"; + + /** + * Flag indicating whether the carrier supports RCS presence indication for video calls. When + * {@code true}, the carrier supports RCS presence indication for video calls. When presence + * is supported, the device should use the + * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the + * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate + * whether each contact supports video calling. The UI is made aware that presence is enabled + * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE} + * and can choose to hide or show the video calling icon based on whether a contact supports + * video. + * + * @hide + */ + @SystemApi + public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -628,6 +654,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false); sDefaults.putBoolean(BOOL_ALLOW_EMERGENCY_VIDEO_CALLS, false); sDefaults.putBoolean(BOOL_ALLOW_VIDEO_PAUSE, true); + sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); @@ -662,6 +689,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, ""); sDefaults.putString(KEY_MMS_USER_AGENT_STRING, ""); sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true); + sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false); } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 99989377c374..39f321365c8d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.net.Uri; +import android.os.Looper; import android.telephony.Rlog; import android.os.Handler; import android.os.Message; @@ -347,7 +348,31 @@ public class SubscriptionManager { * for #onSubscriptionsChanged to be invoked. */ public static class OnSubscriptionsChangedListener { - private final Handler mHandler = new Handler() { + private final Handler mHandler; + + public OnSubscriptionsChangedListener() { + mHandler = new OnSubscriptionsChangedListenerHandler(); + } + + /** + * Contructor that takes in looper as parameter in case a subclass/instantiation needs + * to use a specific looper (like in tests where mainLooper may need to be used). + * @param looper Looper to be used for mHandler + * @hide + */ + protected OnSubscriptionsChangedListener(Looper looper) { + mHandler = new OnSubscriptionsChangedListenerHandler(looper); + } + + private class OnSubscriptionsChangedListenerHandler extends Handler { + private OnSubscriptionsChangedListenerHandler() { + super(); + } + + private OnSubscriptionsChangedListenerHandler(Looper looper) { + super(looper); + } + @Override public void handleMessage(Message msg) { if (DBG) { @@ -355,7 +380,7 @@ public class SubscriptionManager { } OnSubscriptionsChangedListener.this.onSubscriptionsChanged(); } - }; + } /** * Callback invoked when there is any change to any SubscriptionInfo. Typically |