Merge "Do not show volume expander when there is no touch feature"
diff --git a/Android.mk b/Android.mk
index c1c74ea..b95ab2e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -345,6 +345,7 @@
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 b0eb006..629d832 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5793,6 +5793,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 bd7dc97..6a36ce0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5919,6 +5919,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 13d98f4..21b101f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5795,6 +5795,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 81e00ff..4e55c89 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2015,7 +2015,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 a73ad09..e163b1c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -409,9 +409,6 @@
// 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 @@
* for the duration of {@link #onResume()} and {@link #onPause()} as well.
*/
final public boolean isResumed() {
- return mResumed;
+ return mState >= RESUMED;
}
/**
@@ -1630,7 +1627,6 @@
mWho = null;
mAdded = false;
mRemoving = false;
- mResumed = false;
mFromLayout = false;
mInLayout = false;
mRestored = false;
@@ -2113,7 +2109,6 @@
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 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
+ mState = CREATED;
mCalled = false;
onCreate(savedInstanceState);
if (!mCalled) {
@@ -2238,6 +2234,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
+ mState = ACTIVITY_CREATED;
mCalled = false;
onActivityCreated(savedInstanceState);
if (!mCalled) {
@@ -2254,6 +2251,7 @@
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions();
}
+ mState = STARTED;
mCalled = false;
onStart();
if (!mCalled) {
@@ -2273,6 +2271,7 @@
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions();
}
+ mState = RESUMED;
mCalled = false;
onResume();
if (!mCalled) {
@@ -2389,6 +2388,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchPause();
}
+ mState = STARTED;
mCalled = false;
onPause();
if (!mCalled) {
@@ -2401,6 +2401,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchStop();
}
+ mState = STOPPED;
mCalled = false;
onStop();
if (!mCalled) {
@@ -2428,6 +2429,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchDestroyView();
}
+ mState = CREATED;
mCalled = false;
onDestroyView();
if (!mCalled) {
@@ -2443,6 +2445,7 @@
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 696ccdb..84ae09d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1004,7 +1004,6 @@
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 @@
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 @@
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 @@
}
}
- 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 08e9b1e..4de3ceb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3540,6 +3540,66 @@
}
/**
+ * 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 754cb43..d3c32c5 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -202,6 +202,9 @@
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 38abac7..5113e19 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4413,6 +4413,15 @@
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 1349662..a0df610 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -463,92 +463,60 @@
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 91fdf7f..38e0044 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 @@
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 f642f08..2826882 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1258,6 +1258,10 @@
*/
@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 cabc6fa..176e923 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,7 @@
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 @@
}
/**
+ * 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 d4dd669..ef91137 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -165,4 +165,6 @@
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 fdd34f5..e58744b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -130,6 +130,9 @@
/** 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 @@
/** 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 @@
/** 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 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 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 @@
* Return total PSS memory usage in kB.
*/
public int getTotalPss() {
- return dalvikPss + nativePss + otherPss;
+ return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
}
/**
@@ -274,7 +289,8 @@
}
/**
- * 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 @@
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 @@
}
/** @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 @@
* 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 @@
dest.writeInt(otherPrivateClean);
dest.writeInt(otherSharedClean);
dest.writeInt(otherSwappedOut);
+ dest.writeInt(hasSwappedOutPss ? 1 : 0);
+ dest.writeInt(otherSwappedOutPss);
dest.writeIntArray(otherStats);
}
@@ -689,6 +732,8 @@
otherPrivateClean = source.readInt();
otherSharedClean = source.readInt();
otherSwappedOut = source.readInt();
+ hasSwappedOutPss = source.readInt() != 0;
+ otherSwappedOutPss = source.readInt();
otherStats = source.createIntArray();
}
@@ -1563,11 +1608,12 @@
/**
* 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 d4c9944..a6485e4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -641,7 +641,7 @@
* 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/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1c24392..f674298 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5359,6 +5359,9 @@
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 @@
&& (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 @@
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 @@
// 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 0fb3951..3eb2e37 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1488,7 +1488,8 @@
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 c79e184..09cf704 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -271,6 +271,10 @@
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 830da79..b3222f0 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -19,6 +19,7 @@
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 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 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 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());
- }
-
- String res = transact(cmd);
+ public void execute(String cmd, Object... args) throws InstallerException {
+ final String resRaw = executeForResult(cmd, args);
+ int res = -1;
try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
+ res = Integer.parseInt(resRaw);
+ } catch (NumberFormatException ignored) {
+ }
+ if (res != 0) {
+ throw new InstallerException(
+ "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
}
}
- public int dexopt(String apkPath, int uid, String instructionSet,
- int dexoptNeeded, int dexFlags) {
- return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
- null /*outputPath*/, dexFlags);
+ 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 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 instructionSet, int dexoptNeeded,
+ int dexFlags) throws InstallerException {
+ dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags);
+ }
+
+ 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 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 4a1f7f4..eecc0ee 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.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 @@
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 f159a4d..b4c4ef5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2677,20 +2677,16 @@
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 16bf9dd..ca1334c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -26,6 +26,7 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -372,6 +373,10 @@
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 @@
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 799ed83..fda0ffa 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -60,19 +60,7 @@
// 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 e162810..8e8f6c3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -152,7 +152,8 @@
static struct {
jmethodID postDynPolicyEventFromNative;
-} gDynPolicyEventHandlerMethods;
+ jmethodID postRecordConfigEventFromNative;
+} gAudioPolicyEventHandlerMethods;
static Mutex gLock;
@@ -378,12 +379,26 @@
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 @@
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 @@
(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 @@
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 2488111..03a1e71 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -84,6 +84,7 @@
jfieldID privateClean_field;
jfieldID sharedClean_field;
jfieldID swappedOut_field;
+ jfieldID swappedOutPss_field;
};
struct stat_field_names {
@@ -94,20 +95,22 @@
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 @@
int privateClean;
int sharedClean;
int swappedOut;
+ int swappedOutPss;
};
#define BINDER_STATS "/proc/binder/stats"
@@ -206,7 +210,7 @@
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 @@
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 @@
int subHeap = HEAP_UNKNOWN;
int prevHeap = HEAP_UNKNOWN;
+ *foundSwapPss = false;
+
if(fgets(line, sizeof(line), fp) == 0) return;
while (!done) {
@@ -340,6 +346,7 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
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 651b453..e2150c0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -82,8 +82,13 @@
*/
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 @@
/**
* @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 @@
/**
* @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 @@
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 (paddingL >= 0) {
+ padding.left = paddingL;
}
- if (layerState.mPaddingBottom >= 0) {
- padding.bottom = layerState.mPaddingBottom;
+ if (paddingT >= 0) {
+ padding.top = paddingT;
}
- final int paddingRtlLeft;
- final int paddingRtlRight;
- if (getLayoutDirection() == LayoutDirection.RTL) {
- paddingRtlLeft = layerState.mPaddingEnd;
- paddingRtlRight = layerState.mPaddingStart;
- } else {
- paddingRtlLeft = layerState.mPaddingStart;
- paddingRtlRight = layerState.mPaddingEnd;
+ if (paddingR >= 0) {
+ padding.right = paddingR;
}
- final int paddingLeft = paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
- if (paddingLeft >= 0) {
- padding.left = paddingLeft;
- }
-
- 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 @@
}
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);
+ final Rect container = mTmpContainer;
+ container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT,
+ bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB);
- // 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);
+ // 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 @@
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 @@
// 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 @@
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/Android.mk b/libs/hwui/Android.mk
index 0932e89..44c5e2f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -109,8 +109,8 @@
BakedOpDispatcher.cpp \
BakedOpRenderer.cpp \
BakedOpState.cpp \
- FrameReorderer.cpp \
- LayerReorderer.cpp \
+ FrameBuilder.cpp \
+ LayerBuilder.cpp \
RecordingCanvas.cpp
hwui_cflags += -DHWUI_NEW_OPS
@@ -237,7 +237,7 @@
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
tests/unit/BakedOpStateTests.cpp \
- tests/unit/FrameReordererTests.cpp \
+ tests/unit/FrameBuilderTests.cpp \
tests/unit/RecordingCanvasTests.cpp
endif
@@ -299,7 +299,7 @@
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
- tests/microbench/FrameReordererBench.cpp
+ tests/microbench/FrameBuilderBench.cpp
endif
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 23aca89..5b34f6b 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -545,19 +545,19 @@
op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshPatchQuads(*mesh)
- .setMeshTexturedUnitQuad(texture->uvMapper)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
- Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
- .build();
- renderer.renderGlop(state, glop);
+ if (CC_LIKELY(texture)) {
+ const AutoTexture autoCleanup(texture);
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshPatchQuads(*mesh)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+ .build();
+ renderer.renderGlop(state, glop);
+ }
}
void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index c1f19a3..42fb66f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -161,7 +161,7 @@
}
void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
- if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+ if (rect.contains(Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight))) {
// Full viewport is being cleared - disable scissor
mRenderState.scissor().setEnabled(false);
} else {
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index f0406fa..87844f9 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -71,7 +71,6 @@
clipState = snapshot.mutateClipArea().serializeClip(allocator);
LOG_ALWAYS_FATAL_IF(!clipState, "clipState required");
clippedBounds = clipState->rect;
- transform.mapRect(clippedBounds);
clipSideFlags = OpClipSideFlags::Full;
}
diff --git a/libs/hwui/FrameReorderer.cpp b/libs/hwui/FrameBuilder.cpp
similarity index 88%
rename from libs/hwui/FrameReorderer.cpp
rename to libs/hwui/FrameBuilder.cpp
index 4bfc0b4..166656c 100644
--- a/libs/hwui/FrameReorderer.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
@@ -30,24 +30,25 @@
namespace android {
namespace uirenderer {
-FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
- mLayerReorderers.reserve(layers.entries().size());
+ mLayerBuilders.reserve(layers.entries().size());
mLayerStack.reserve(layers.entries().size());
// Prepare to defer Fbo0
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+ auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
+ mLayerBuilders.push_back(fbo0);
mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
lightCenter);
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
- // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+ // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
for (int i = layers.entries().size() - 1; i >= 0; i--) {
RenderNode* layerNode = layers.entries()[i].renderNode;
const Rect& layerDamage = layers.entries()[i].damage;
@@ -77,11 +78,11 @@
}
}
-void FrameReorderer::onViewportInitialized() {}
+void FrameBuilder::onViewportInitialized() {}
-void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
const RenderProperties& properties = node.properties();
const Outline& outline = properties.getOutline();
if (properties.getAlpha() <= 0
@@ -213,7 +214,7 @@
}
template <typename V>
-void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
const int size = zTranslatedNodes.size();
if (size == 0
|| (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -263,7 +264,7 @@
}
}
-void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
auto& node = *casterNodeOp.renderNode;
auto& properties = node.properties();
@@ -319,7 +320,7 @@
}
}
-void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -352,15 +353,15 @@
}
/**
- * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
*
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
*/
#define OP_RECEIVER(Type) \
- [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void FrameReorderer::deferNodeOps(const RenderNode& renderNode) {
- typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);
+ [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); },
+void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
+ typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op);
static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
@@ -384,7 +385,7 @@
}
}
-void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
@@ -399,7 +400,7 @@
mCanvasState.restoreToCount(count);
}
-void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
if (!op.skipInOrderDraw) {
deferRenderNodeOpImpl(op);
}
@@ -409,7 +410,7 @@
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
@@ -431,7 +432,7 @@
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
}
-void FrameReorderer::deferArcOp(const ArcOp& op) {
+void FrameBuilder::deferArcOp(const ArcOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
@@ -440,7 +441,7 @@
|| state.computedState.clipState->mode == ClipMode::Rectangle;
}
-void FrameReorderer::deferBitmapOp(const BitmapOp& op) {
+void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -452,7 +453,7 @@
&& PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
&& op.bitmap->colorType() != kAlpha_8_SkColorType
&& hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
// TODO: AssetAtlas in mergeId
currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
} else {
@@ -460,19 +461,19 @@
}
}
-void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
// allocate a temporary oval op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
float x = *(op.x);
@@ -487,29 +488,29 @@
deferOvalOp(*resolvedOp);
}
-void FrameReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
}
-void FrameReorderer::deferLinesOp(const LinesOp& op) {
+void FrameBuilder::deferLinesOp(const LinesOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void FrameReorderer::deferOvalOp(const OvalOp& op) {
+void FrameBuilder::deferOvalOp(const OvalOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferPatchOp(const PatchOp& op) {
+void FrameBuilder::deferPatchOp(const PatchOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
if (bakedState->computedState.transform.isPureTranslate()
&& PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
&& hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
// TODO: AssetAtlas in mergeId
// Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
@@ -520,24 +521,24 @@
}
}
-void FrameReorderer::deferPathOp(const PathOp& op) {
+void FrameBuilder::deferPathOp(const PathOp& op) {
deferStrokeableOp(op, OpBatchType::Bitmap);
}
-void FrameReorderer::deferPointsOp(const PointsOp& op) {
+void FrameBuilder::deferPointsOp(const PointsOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void FrameReorderer::deferRectOp(const RectOp& op) {
+void FrameBuilder::deferRectOp(const RectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
// allocate a temporary round rect op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
@@ -548,7 +549,7 @@
deferRoundRectOp(*resolvedOp);
}
-void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -559,7 +560,7 @@
return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
}
-void FrameReorderer::deferTextOp(const TextOp& op) {
+void FrameBuilder::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -574,19 +575,19 @@
}
}
-void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
}
-void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
}
-void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
float contentTranslateX, float contentTranslateY,
const Rect& repaintRect,
const Vector3& lightCenter,
@@ -601,11 +602,13 @@
repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
// create a new layer repaint, and push its index on the stack
- mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
+ mLayerStack.push_back(mLayerBuilders.size());
+ auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight,
+ repaintRect, beginLayerOp, renderNode);
+ mLayerBuilders.push_back(newFbo);
}
-void FrameReorderer::restoreForLayer() {
+void FrameBuilder::restoreForLayer() {
// restore canvas, and pop finished layer off of the stack
mCanvasState.restore();
mLayerStack.pop_back();
@@ -613,7 +616,7 @@
// TODO: defer time rejection (when bounds become empty) + tests
// Option - just skip layers with no bounds at playback + defer?
-void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
@@ -658,7 +661,7 @@
&op, nullptr);
}
-void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
int finishedLayerIndex = mLayerStack.back();
@@ -671,7 +674,7 @@
beginLayerOp.localMatrix,
beginLayerOp.localClip,
beginLayerOp.paint,
- &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
+ &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
@@ -681,12 +684,12 @@
// Layer won't be drawn - delete its drawing batches to prevent it from doing any work
// TODO: need to prevent any render work from being done
// - create layerop earlier for reject purposes?
- mLayerReorderers[finishedLayerIndex].clear();
+ mLayerBuilders[finishedLayerIndex]->clear();
return;
}
}
-void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
boundsTransform.multiply(op.localMatrix);
@@ -721,7 +724,7 @@
currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
}
-void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
diff --git a/libs/hwui/FrameReorderer.h b/libs/hwui/FrameBuilder.h
similarity index 89%
rename from libs/hwui/FrameReorderer.h
rename to libs/hwui/FrameBuilder.h
index 562e6a1..3ba73f0 100644
--- a/libs/hwui/FrameReorderer.h
+++ b/libs/hwui/FrameBuilder.h
@@ -19,7 +19,7 @@
#include "BakedOpState.h"
#include "CanvasState.h"
#include "DisplayList.h"
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
#include "RecordedOp.h"
#include <vector>
@@ -42,7 +42,7 @@
* Resolves final drawing state for each operation (including clip, alpha and matrix), and then
* reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
* from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
- * draw stream) will create different reorder contexts, each in its own LayerReorderer.
+ * draw stream) will create different reorder contexts, each in its own LayerBuilder.
*
* Then the prepared or 'baked' drawing commands can be issued by calling the templated
* replayBakedOps() function, which will dispatch them (including any created merged op collections)
@@ -52,13 +52,13 @@
* This class is also the authoritative source for traversing RenderNodes, both for standard op
* traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
*/
-class FrameReorderer : public CanvasStateClient {
+class FrameBuilder : public CanvasStateClient {
public:
- FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
- virtual ~FrameReorderer() {}
+ virtual ~FrameBuilder() {}
/**
* replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -98,8 +98,8 @@
// Relay through layers in reverse order, since layers
// later in the list will be drawn by earlier ones
- for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
- LayerReorderer& layer = mLayerReorderers[i];
+ for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
+ LayerBuilder& layer = *(mLayerBuilders[i]);
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
@@ -112,15 +112,15 @@
}
}
- const LayerReorderer& fbo0 = mLayerReorderers[0];
+ const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
renderer.endFrame(fbo0.repaintRect);
}
void dump() const {
- for (auto&& layer : mLayerReorderers) {
- layer.dump();
+ for (auto&& layer : mLayerBuilders) {
+ layer->dump();
}
}
@@ -143,7 +143,7 @@
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
- LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+ LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); }
BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
@@ -173,7 +173,7 @@
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
- * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.
+ * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
*
* These private methods are called from within deferImpl to defer each individual op
* type differently.
@@ -183,17 +183,17 @@
#undef X
// List of every deferred layer's render state. Replayed in reverse order to render a frame.
- std::vector<LayerReorderer> mLayerReorderers;
+ std::vector<LayerBuilder*> mLayerBuilders;
/*
- * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+ * Stack of indices within mLayerBuilders representing currently active layers. If drawing
* layerA within a layerB, will contain, in order:
* - 0 (representing FBO 0, always present)
* - layerB's index
* - layerA's index
*
- * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
- * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+ * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a
+ * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index
* won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
* ops added to it.
*/
diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerBuilder.cpp
similarity index 95%
rename from libs/hwui/LayerReorderer.cpp
rename to libs/hwui/LayerBuilder.cpp
index 9a17e93..7170d4f 100644
--- a/libs/hwui/LayerReorderer.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
#include "BakedOpState.h"
#include "RenderNode.h"
@@ -53,7 +53,7 @@
void dump() const {
ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
- this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+ this, mBatchId, mMerging, (int) mOps.size(), RECT_ARGS(mBounds));
}
protected:
batchid_t mBatchId;
@@ -202,7 +202,7 @@
int mClipSideFlags;
};
-LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+LayerBuilder::LayerBuilder(uint32_t width, uint32_t height,
const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
: width(width)
, height(height)
@@ -214,7 +214,7 @@
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
-void LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
BatchBase** targetBatch, size_t* insertBatchIndex) const {
for (int i = mBatches.size() - 1; i >= 0; i--) {
BatchBase* overBatch = mBatches[i];
@@ -237,11 +237,11 @@
}
}
-void LayerReorderer::deferLayerClear(const Rect& rect) {
+void LayerBuilder::deferLayerClear(const Rect& rect) {
mClearRects.push_back(rect);
}
-void LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
+void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
if (CC_UNLIKELY(!mClearRects.empty())) {
const int vertCount = mClearRects.size() * 4;
// put the verts in the frame allocator, since
@@ -269,13 +269,11 @@
verts, vertCount);
BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
&viewportClip, bounds, *op);
-
-
deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
}
}
-void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId) {
if (batchId != OpBatchType::CopyToLayer) {
// if first op after one or more unclipped saveLayers, flush the layer clears
@@ -300,7 +298,7 @@
}
}
-void LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
if (batchId != OpBatchType::CopyToLayer) {
// if first op after one or more unclipped saveLayers, flush the layer clears
@@ -332,7 +330,7 @@
}
}
-void LayerReorderer::replayBakedOpsImpl(void* arg,
+void LayerBuilder::replayBakedOpsImpl(void* arg,
BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
ATRACE_NAME("flush drawing commands");
for (const BatchBase* batch : mBatches) {
@@ -355,8 +353,8 @@
}
}
-void LayerReorderer::dump() const {
- ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+void LayerBuilder::dump() const {
+ ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
this, width, height, offscreenBuffer, beginLayerOp, renderNode);
for (const BatchBase* batch : mBatches) {
batch->dump();
diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerBuilder.h
similarity index 87%
rename from libs/hwui/LayerReorderer.h
rename to libs/hwui/LayerBuilder.h
index 83cda81..99968e1 100644
--- a/libs/hwui/LayerReorderer.h
+++ b/libs/hwui/LayerBuilder.h
@@ -18,6 +18,7 @@
#include "ClipArea.h"
#include "Rect.h"
+#include "utils/Macros.h"
#include <vector>
#include <unordered_map>
@@ -66,15 +67,17 @@
* Stores the deferred render operations and state used to compute ordering
* for a single FBO/layer.
*/
-class LayerReorderer {
+class LayerBuilder {
+// Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip
+PREVENT_COPY_AND_ASSIGN(LayerBuilder);
public:
- // Create LayerReorderer for Fbo0
- LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
- : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
+ // Create LayerBuilder for Fbo0
+ LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect)
+ : LayerBuilder(width, height, repaintRect, nullptr, nullptr) {};
- // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+ // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a
// saveLayer, renderNode is present for a HW layer.
- LayerReorderer(uint32_t width, uint32_t height,
+ LayerBuilder(uint32_t width, uint32_t height,
const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index be612d2..5b1a854 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -42,7 +42,7 @@
LayerUpdateQueue() {}
void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
void clear();
- const std::vector<Entry> entries() const { return mEntries; }
+ const std::vector<Entry>& entries() const { return mEntries; }
private:
std::vector<Entry> mEntries;
};
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 612cdfd..8e4a3df 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -47,11 +47,11 @@
class DisplayListCanvas;
class DisplayListOp;
class OpenGLRenderer;
-class OpReorderer;
class Rect;
class SkiaShader;
#if HWUI_NEW_OPS
+class FrameBuilder;
class OffscreenBuffer;
struct RenderNodeOp;
typedef OffscreenBuffer layer_t;
@@ -87,7 +87,7 @@
*/
class RenderNode : public VirtualLightRefBase {
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class FrameReorderer;
+friend class FrameBuilder;
public:
enum DirtyPropertyMask {
GENERIC = 1 << 1,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 130cc80..976f775 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -162,15 +162,15 @@
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 0089fb5..e342d19 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -47,7 +47,7 @@
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/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fff8e09..644f356 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,7 @@
#include "utils/TimeUtils.h"
#if HWUI_NEW_OPS
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#endif
#include <cutils/properties.h>
@@ -338,14 +338,13 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+ FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
mRenderNodes, mLightCenter);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
mOpaque, mLightInfo);
// TODO: profiler().draw(mCanvas);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
-
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
bool drew = renderer.didDraw();
#else
diff --git a/libs/hwui/tests/microbench/FrameReordererBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
similarity index 90%
rename from libs/hwui/tests/microbench/FrameReordererBench.cpp
rename to libs/hwui/tests/microbench/FrameBuilderBench.cpp
index b4c9a36..67c95e2 100644
--- a/libs/hwui/tests/microbench/FrameReordererBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -19,7 +19,7 @@
#include "BakedOpState.h"
#include "BakedOpDispatcher.h"
#include "BakedOpRenderer.h"
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#include "LayerUpdateQueue.h"
#include "RecordedOp.h"
#include "RecordingCanvas.h"
@@ -66,9 +66,9 @@
auto nodes = createTestNodeList();
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
- MicroBench::DoNotOptimize(&reorderer);
+ MicroBench::DoNotOptimize(&frameBuilder);
}
StopBenchmarkTiming();
}
@@ -84,11 +84,11 @@
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
StopBenchmarkTiming();
@@ -117,10 +117,10 @@
auto nodes = getSyncedSceneNodes(sceneName);
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
- MicroBench::DoNotOptimize(&reorderer);
+ MicroBench::DoNotOptimize(&frameBuilder);
}
benchmark.StopBenchmarkTiming();
}
@@ -136,12 +136,12 @@
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
benchmark.StopBenchmarkTiming();
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index b350686..0f8e047 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -199,9 +199,12 @@
}
TEST(BakedOpState, tryShadowOpConstruct) {
+ Matrix4 translate10x20;
+ translate10x20.loadTranslate(10, 20, 0);
+
LinearAllocator allocator;
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+ auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
@@ -209,11 +212,14 @@
"since op is quick rejected based on snapshot clip";
}
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
+
+ EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
+ EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
}
}
diff --git a/libs/hwui/tests/unit/FrameReordererTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
similarity index 90%
rename from libs/hwui/tests/unit/FrameReordererTests.cpp
rename to libs/hwui/tests/unit/FrameBuilderTests.cpp
index 9d2eb98..bded50a 100644
--- a/libs/hwui/tests/unit/FrameReordererTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -18,7 +18,7 @@
#include <BakedOpState.h>
#include <DeferredLayerUpdater.h>
-#include <FrameReorderer.h>
+#include <FrameBuilder.h>
#include <LayerUpdateQueue.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
@@ -113,7 +113,7 @@
class FailRenderer : public TestRendererBase {};
-TEST(FrameReorderer, simple) {
+TEST(FrameBuilder, simple) {
class SimpleTestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -138,14 +138,14 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-TEST(FrameReorderer, simpleStroke) {
+TEST(FrameBuilder, simpleStroke) {
class SimpleStrokeTestRenderer : public TestRendererBase {
public:
void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +164,14 @@
strokedPaint.setStrokeWidth(10);
canvas.drawPoint(50, 50, strokedPaint);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleStrokeTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(FrameReorderer, simpleRejection) {
+TEST(FrameBuilder, simpleRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +179,14 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, simpleBatching) {
+TEST(FrameBuilder, simpleBatching) {
const int LOOPS = 5;
class SimpleBatchingTestRenderer : public TestRendererBase {
public:
@@ -214,15 +214,15 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SimpleBatchingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
<< "Expect number of ops = 2 * loop count";
}
-TEST(FrameReorderer, clippedMerging) {
+TEST(FrameBuilder, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +255,14 @@
canvas.drawBitmap(bitmap, 40, 70, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(node), sLightCenter);
ClippedMergingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, textMerging) {
+TEST(FrameBuilder, textMerging) {
class TextMergingTestRenderer : public TestRendererBase {
public:
void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +283,14 @@
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
createSyncedNodeList(node), sLightCenter);
TextMergingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
}
-TEST(FrameReorderer, textStrikethrough) {
+TEST(FrameBuilder, textStrikethrough) {
const int LOOPS = 5;
class TextStrikethroughTestRenderer : public TestRendererBase {
public:
@@ -314,15 +314,15 @@
TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
}
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
createSyncedNodeList(node), sLightCenter);
TextStrikethroughTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
<< "Expect number of ops = 2 * loop count";
}
-RENDERTHREAD_TEST(FrameReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
class TextureLayerTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -348,14 +348,14 @@
canvas.drawLayer(layerUpdater.get());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
TextureLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(FrameReorderer, renderNode) {
+TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,13 +393,13 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
RenderNodeTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, clipped) {
+TEST(FrameBuilder, clipped) {
class ClippedTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -416,14 +416,14 @@
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, createSyncedNodeList(node), sLightCenter);
ClippedTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, saveLayer_simple) {
+TEST(FrameBuilder, saveLayer_simple) {
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -459,14 +459,14 @@
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayer_nested) {
+TEST(FrameBuilder, saveLayer_nested) {
/* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
* - startTemporaryLayer2, rect2 endLayer2
* - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -531,14 +531,14 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
createSyncedNodeList(node), sLightCenter);
SaveLayerNestedTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayer_contentRejection) {
+TEST(FrameBuilder, saveLayer_contentRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,15 +551,15 @@
canvas.restore();
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, saveLayerUnclipped_simple) {
+TEST(FrameBuilder, saveLayerUnclipped_simple) {
class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -594,14 +594,14 @@
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {
+TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -648,10 +648,10 @@
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restoreToCount(restoreTo);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedMergedClearsTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex())
<< "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
}
@@ -660,7 +660,7 @@
* - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
* - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
*/
-TEST(FrameReorderer, saveLayerUnclipped_complex) {
+TEST(FrameBuilder, saveLayerUnclipped_complex) {
class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -710,14 +710,14 @@
canvas.restore();
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedComplexTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(12, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -768,17 +768,17 @@
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedNodeList, sLightCenter);
HwLayerSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startRepaintLayer(child), rect(grey), endLayer
* - startTemporaryLayer, drawLayer(child), endLayer
@@ -869,10 +869,10 @@
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, sLightCenter);
HwLayerComplexTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(13, renderer.getIndex());
// clean up layer pointers, so we can safely destruct RenderNodes
@@ -894,7 +894,7 @@
node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
-TEST(FrameReorderer, zReorder) {
+TEST(FrameBuilder, zReorder) {
class ZReorderTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -918,14 +918,14 @@
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ZReorderTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
};
-TEST(FrameReorderer, projectionReorder) {
+TEST(FrameBuilder, projectionReorder) {
static const int scrollX = 5;
static const int scrollY = 10;
class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1001,10 +1001,10 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ProjectionReorderTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(3, renderer.getIndex());
}
@@ -1020,7 +1020,7 @@
});
}
-TEST(FrameReorderer, shadow) {
+TEST(FrameBuilder, shadow) {
class ShadowTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1044,14 +1044,14 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
}
-TEST(FrameReorderer, shadowSaveLayer) {
+TEST(FrameBuilder, shadowSaveLayer) {
class ShadowSaveLayerTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1085,14 +1085,14 @@
canvas.restoreToCount(count);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
ShadowSaveLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
class ShadowHwLayerTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1135,17 +1135,17 @@
auto syncedList = createSyncedNodeList(parent);
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, (Vector3) { 100, 100, 100 });
ShadowHwLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
*layerHandle = nullptr;
}
-TEST(FrameReorderer, shadowLayering) {
+TEST(FrameBuilder, shadowLayering) {
class ShadowLayeringTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1164,10 +1164,10 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowLayeringTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
@@ -1192,14 +1192,14 @@
canvas.drawRect(0, 0, 100, 100, paint);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
createSyncedNodeList(node), sLightCenter);
PropertyTestRenderer renderer(opValidateCallback);
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
}
-TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
testProperty([](RenderProperties& properties) {
properties.setAlpha(0.5f);
properties.setHasOverlappingRendering(false);
@@ -1208,7 +1208,7 @@
});
}
-TEST(FrameReorderer, renderPropClipping) {
+TEST(FrameBuilder, renderPropClipping) {
testProperty([](RenderProperties& properties) {
properties.setClipToBounds(true);
properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1218,7 +1218,7 @@
});
}
-TEST(FrameReorderer, renderPropRevealClip) {
+TEST(FrameBuilder, renderPropRevealClip) {
testProperty([](RenderProperties& properties) {
properties.mutableRevealClip().set(true, 50, 50, 25);
}, [](const RectOp& op, const BakedOpState& state) {
@@ -1229,7 +1229,7 @@
});
}
-TEST(FrameReorderer, renderPropOutlineClip) {
+TEST(FrameBuilder, renderPropOutlineClip) {
testProperty([](RenderProperties& properties) {
properties.mutableOutline().setShouldClip(true);
properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1241,7 +1241,7 @@
});
}
-TEST(FrameReorderer, renderPropTransform) {
+TEST(FrameBuilder, renderPropTransform) {
testProperty([](RenderProperties& properties) {
properties.setLeftTopRightBottom(10, 10, 110, 110);
@@ -1334,15 +1334,15 @@
});
auto nodes = createSyncedNodeList(node); // sync before querying height
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
SaveLayerAlphaClipTestRenderer renderer(outObservedData);
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
// assert, since output won't be valid if we haven't seen a save layer triggered
ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setTranslationX(10); // offset rendering content
@@ -1358,7 +1358,7 @@
<< "expect content to be translated as part of being clipped";
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
// Translate and rotate the view so that the only visible part is the top left corner of
@@ -1377,7 +1377,7 @@
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setPivotX(0);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a092408..ea1690f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -51,6 +51,7 @@
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 @@
}
/**
- * 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 @@
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 @@
}
+ //====================================================================
+ // Audio policy
/**
* @hide
* Register the given {@link AudioPolicy}.
@@ -2754,6 +2795,131 @@
}
+ //====================================================================
+ // 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 0000000..afe912b
--- /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 0000000..aefe692
--- /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 7bfd7ca..aa0d78d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -227,7 +227,7 @@
}
/**
- * 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 @@
}
}
+ /**
+ * 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 @@
// 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 dbb7661..abe92c7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,9 +20,11 @@
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 @@
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 0000000..a5eb8b9f
--- /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 478fd99..f1f8437 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2175,8 +2175,6 @@
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 9bcb5e3..0fba992 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1735,8 +1735,7 @@
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 @@
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 a102e51..b2fa0ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -92,8 +92,6 @@
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 6ec16db..dc89a56 100644
--- a/media/java/android/mtp/MtpEvent.java
+++ b/media/java/android/mtp/MtpEvent.java
@@ -18,15 +18,152 @@
/**
* 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 49b579c..2004a3a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -85,6 +85,13 @@
jmethodID setNativeObjectLocked;
} gPersistentSurfaceClassInfo;
+static struct {
+ jint Unencrypted;
+ jint AesCtr;
+ jint AesCbc;
+} gCryptoModes;
+
+
struct fields_t {
jfieldID context;
jmethodID postEventFromNativeID;
@@ -94,6 +101,9 @@
jfieldID cryptoInfoKeyID;
jfieldID cryptoInfoIVID;
jfieldID cryptoInfoModeID;
+ jfieldID cryptoInfoPatternID;
+ jfieldID patternEncryptBlocksID;
+ jfieldID patternSkipBlocksID;
};
static fields_t gFields;
@@ -325,11 +335,12 @@
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 @@
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 @@
index, offset,
subSamples, numSubSamples,
(const uint8_t *)key, (const uint8_t *)iv,
- (CryptoPlugin::Mode)mode,
+ mode,
+ pattern,
timestampUs,
flags,
&errorDetailMsg);
@@ -1658,6 +1689,22 @@
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 @@
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 6650cf9..c0c47ef 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -81,6 +81,7 @@
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 4aa12c2..130dfe5 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -98,6 +98,9 @@
// 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 @@
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 @@
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 abb464e..6fb8b51 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,7 +56,6 @@
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 @@
@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 f9bbccb..1b5911d 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 cacdf4d..5adb165 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 898713f..22e81c6 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 @@
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 @@
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 d1f8ff7..0000000
--- 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 3ee1d42..2485ad9 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 @@
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 @@
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 c216c77..15b8ef3 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.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 49b48c5..7527f54 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 @@
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 @@
@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 @@
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 c860668..2189b55 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -3,9 +3,22 @@
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
+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_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 0000000..b3d7cf9
--- /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 0000000..f7a9c9f
--- /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 0000000..46267a2
--- /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 0000000..c090468
--- /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 d7c78f6..9a1d6a4 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 ac19cf5..6dfa9ad 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 0000000..c2f885d
--- /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 0000000..e63130d
--- /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 0000000..f6caaa9
--- /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 0000000..569017a
--- /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 0000000..f041504
--- /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 0000000..308477b0
--- /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 0000000..f1beb10
--- /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/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 51cae86..fdb0d32 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 @@
// 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/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 28121b4..e310801 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 @@
// 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 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapDetected = false;
+ mSecondFingerDoubleTap = false;
mRecognizingGesture = true;
mPreviousX = x;
mPreviousY = y;
@@ -133,6 +142,43 @@
}
}
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 @@
public void clear() {
mFirstTapDetected = false;
mDoubleTapDetected = false;
+ mSecondFingerDoubleTap = false;
cancelGesture();
mStrokeBuffer.clear();
}
@@ -229,4 +276,28 @@
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 65a27c8..37a6c02 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4848,6 +4848,11 @@
}
@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 a4b13ed..093a33d 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.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.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 @@
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 @@
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 @@
}
int num = 0;
- long[] tmp = new long[1];
+ long[] tmp = new long[2];
do {
ProcessRecord proc;
int procState;
@@ -2269,7 +2274,7 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
dumpDetails = true;
dumpFullDetails = true;
dumpDalvik = true;
+ dumpSwapPss = true;
} else if ("-d".equals(opt)) {
dumpDalvik = true;
} else if ("-c".equals(opt)) {
@@ -15482,22 +15547,29 @@
} 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 @@
}
}
- final boolean isCheckinRequest = scanArgs(args, "--checkin");
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
final long[] tmpLong = new long[1];
@@ -15578,18 +15649,28 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
* 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 @@
/**
* 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 @@
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 @@
// 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/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0397553..f2e8d09 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 @@
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 4bfe300..b4aa4cf 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 @@
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 @@
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 b8cbecb..9331dd8 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.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 @@
LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+ mRecordMonitor.initMonitor();
}
public void systemReady() {
@@ -6165,7 +6169,7 @@
}
//======================
- // 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 @@
}
}
}
+ }
+ //======================
+ // 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 0000000..5806f3f
--- /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 3a10dbe..4504bdb 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 @@
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 @@
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 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 @@
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 @@
// 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 99a051a..190eca6 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.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 @@
/** 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 @@
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;
- }
+ 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);
}
- @Deprecated
- public int install(String name, int uid, int gid, String seinfo) {
- return install(null, name, uid, gid, seinfo);
+ 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 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 void clearAppData(String uuid, String pkgname, int userid, int flags)
+ throws InstallerException {
+ mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags);
}
- 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 void destroyAppData(String uuid, String pkgname, int userid, int flags)
+ throws InstallerException {
+ mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags);
}
- 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 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 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 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 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 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());
- }
-
- @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 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);
+ }
+
+ public void idmap(String targetApkPath, String overlayApkPath, int uid)
+ throws InstallerException {
+ mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
+ }
+
+ public void rmdex(String codePath, String instructionSet) throws InstallerException {
+ assertValidInstructionSet(instructionSet);
+ mInstaller.execute("rmdex", codePath, instructionSet);
+ }
+
+ public void rmPackageDir(String packageDir) throws InstallerException {
+ mInstaller.execute("rmpackagedir", packageDir);
+ }
+
+ public void createUserConfig(int userid) throws InstallerException {
+ mInstaller.execute("mkuserconfig", userid);
+ }
+
+ 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 void moveFiles() throws InstallerException {
+ mInstaller.execute("movefiles");
}
/**
- * 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
+ * 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.
*/
- 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;
- }
-
- 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 linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
+ int userId) throws InstallerException {
+ mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId);
}
- @Deprecated
- public boolean restoreconData(String pkgName, String seinfo, int uid) {
- return restoreconData(null, pkgName, seinfo, uid);
+ public void createOatDir(String oatDir, String dexInstructionSet)
+ throws InstallerException {
+ mInstaller.execute("createoatdir", oatDir, dexInstructionSet);
}
- 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 linkFile(String relativePath, String fromBase, String toBase)
+ throws InstallerException {
+ mInstaller.execute("linkfile", relativePath, fromBase, toBase);
}
- 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 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());
- }
-
- /**
- * Returns true iff. {@code instructionSet} is a valid instruction set.
- */
- private static boolean isValidInstructionSet(String instructionSet) {
- if (instructionSet == null) {
- return false;
- }
-
+ 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 d29a623..b45a922 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.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 @@
| (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 @@
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 55b8bf2..23a58d0 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 @@
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 1655cb6..b84ffa3 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 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 @@
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 @@
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 @@
}
}
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 a41636e..d04eedc 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 @@
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 41627fd..f777faf 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.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 @@
}
} 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 @@
| 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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
: 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 @@
&& (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 @@
}
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 @@
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,50 +6721,65 @@
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) {
+ try {
+ mInstaller.destroyAppData(volumeUuid, packageName, user, flags);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete data directory", e);
+ res = false;
+ }
+ }
+ return res;
+ }
+
+ 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();
+ }
+ }
+
+ 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) {
- 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.clearAppData(volumeUuid, packageName, user,
+ flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete code cache directory", e);
}
}
}
- 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;
- }
- }
-
- return res;
- }
-
- private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
- int[] users = sUserManager.getUserIds();
- int res = 0;
- for (int user : users) {
- int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user);
- if (resInner < 0) {
- res = resInner;
- }
- }
- return res;
- }
-
private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
@@ -7256,6 +7278,8 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
if (packageName != null) {
removeDataDirsLI(volumeUuid, packageName);
}
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
- }
+ removeCodePathLI(file);
}
}
}
@@ -16987,7 +17017,11 @@
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 @@
/** 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 3f9ce7a..9fef515 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 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 @@
int userHandle) {
String[] volumeUuids;
String[] names;
- int[] uids;
+ int[] appIds;
String[] seinfos;
int packagesCount;
synchronized (mPackages) {
@@ -3676,7 +3677,7 @@
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 @@
// 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 @@
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 @@
}
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
- return isEnabledLPr(componentInfo, flags, userId)
- && isMatchLPr(componentInfo, flags);
- }
+ final PackageSetting ps = mPackages.get(componentInfo.packageName);
+ if (ps == null) return false;
- 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 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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dd58b3c..380763a 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
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 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
}
@@ -2468,7 +2480,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policyData = getUserData(userHandle);
return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -2480,7 +2492,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (administrator == null) {
@@ -2497,7 +2509,7 @@
return Collections.EMPTY_LIST;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -2517,7 +2529,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -2535,7 +2547,7 @@
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
@@ -2594,7 +2606,7 @@
if (!mHasFeature) {
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -2664,7 +2676,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2711,7 +2723,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2771,7 +2783,7 @@
if (!mHasFeature) {
return 0L;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
long timeout = 0L;
@@ -2898,7 +2910,7 @@
if (!mHasFeature) {
return 0L;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
return getPasswordExpirationLocked(who, userHandle);
}
@@ -2926,7 +2938,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2970,7 +2982,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3017,7 +3029,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3067,7 +3079,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3117,7 +3129,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3167,7 +3179,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3200,7 +3212,7 @@
if (!mHasFeature) {
return true;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int id = getCredentialOwner(userHandle);
@@ -3272,7 +3284,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle)
: getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
@@ -3285,7 +3297,7 @@
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 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
long time = 0;
@@ -3906,7 +3918,7 @@
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 @@
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4014,7 +4026,7 @@
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 @@
@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 @@
@Override
public void reportSuccessfulPasswordAttempt(int userHandle) {
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4209,7 +4221,7 @@
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 @@
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 @@
if (!mHasFeature) {
// Ok to return current status.
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
return getEncryptionStatus();
}
@@ -4624,7 +4636,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
@@ -5185,17 +5197,28 @@
}
}
- 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 @@
return null;
}
Preconditions.checkNotNull(agent, "agent null");
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
final String componentName = agent.flattenToString();
@@ -6068,7 +6091,7 @@
@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 @@
@Override
public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
- enforceCrossUserPermission(userId);
+ enforceFullCrossUsersPermission(userId);
if (!mHasFeature) {
return null;
}
@@ -6332,7 +6355,7 @@
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
if (admin.disableCallerId != disabled) {
admin.disableCallerId = disabled;
- saveSettingsLocked(UserHandle.getCallingUserId());
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
}
@@ -6352,8 +6375,7 @@
@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 @@
}
@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 2e0866c..5abb6e7 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 @@
// 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 4198217..78edc4d 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_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 @@
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 27deb72..27d5207 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.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 @@
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 72621a4..630367d 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 @@
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 @@
initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
mDatabase.isFirstUpdate());
}
+
+ refreshAppIdleRollingWindow(currentTimeMillis);
}
/**
@@ -170,6 +173,7 @@
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
loadActiveStats(newTime, resetBeginIdleTime);
+ refreshAppIdleRollingWindow(newTime);
}
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -211,6 +215,11 @@
}
}
+ if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+ mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+ }
+
notifyStatsChanged();
}
@@ -222,6 +231,7 @@
for (IntervalStats stats : mCurrentStats) {
stats.updateBeginIdleTime(packageName, beginIdleTime);
}
+ mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
notifyStatsChanged();
}
@@ -229,6 +239,7 @@
for (IntervalStats stats : mCurrentStats) {
stats.updateSystemLastUsedTime(packageName, lastUsedTime);
}
+ mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
notifyStatsChanged();
}
@@ -387,9 +398,8 @@
}
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 @@
}
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 @@
}
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 @@
}
}
}
+
mStatsChanged = false;
updateRolloverDeadline();
}
@@ -516,6 +528,68 @@
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 @@
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 b18af33..b56ce73 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -687,7 +687,7 @@
.append("] PhoneAccount: ")
.append(mAccountHandle)
.append(" Capabilities: ")
- .append(mCapabilities)
+ .append(capabilitiesToString(mCapabilities))
.append(" Schemes: ");
for (String scheme : mSupportedUriSchemes) {
sb.append(scheme)
@@ -698,4 +698,42 @@
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 dabf706..216603c 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 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 @@
*
* @param videoState The video state.
*/
- public VideoProfile(int videoState) {
+ public VideoProfile(@VideoState int videoState) {
this(videoState, QUALITY_DEFAULT);
}
@@ -117,7 +135,7 @@
* @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 @@
* {@link VideoProfile#STATE_RX_ENABLED},
* {@link VideoProfile#STATE_PAUSED}.
*/
+ @VideoState
public int getVideoState() {
return mVideoState;
}
@@ -139,6 +158,7 @@
* 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 6ffc026..80c5b1e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -473,6 +473,15 @@
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 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 @@
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 @@
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 9998937..39f3213 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
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 @@
* 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 @@
}
OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
}
- };
+ }
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically