summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt17
-rw-r--r--api/removed.txt4
-rw-r--r--api/system-current.txt19
-rw-r--r--api/system-removed.txt4
-rw-r--r--api/test-current.txt17
-rw-r--r--api/test-removed.txt4
-rw-r--r--core/java/android/app/ActivityManager.java6
-rw-r--r--core/java/android/content/pm/LauncherActivityInfo.java47
-rw-r--r--core/java/android/os/HardwarePropertiesManager.java13
-rw-r--r--core/java/android/webkit/WebViewProviderInfo.java126
-rw-r--r--core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java89
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp38
-rw-r--r--libs/hwui/Glop.h2
-rw-r--r--libs/hwui/GlopBuilder.cpp12
-rw-r--r--libs/hwui/JankTracker.cpp97
-rw-r--r--libs/hwui/JankTracker.h12
-rw-r--r--libs/hwui/OpenGLRenderer.cpp2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp2
-rw-r--r--libs/hwui/tests/unit/BakedOpDispatcherTests.cpp2
-rw-r--r--libs/hwui/tests/unit/GlopBuilderTests.cpp4
-rw-r--r--media/java/android/media/tv/TvInputManager.java69
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java26
-rw-r--r--services/core/java/com/android/server/GraphicsStatsService.java4
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java619
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java80
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java6
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateService.java179
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUtilityImpl.java8
-rw-r--r--services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp10
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java14
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java251
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java23
40 files changed, 1391 insertions, 547 deletions
diff --git a/api/current.txt b/api/current.txt
index e4615e99f9de..e743881e63aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28707,6 +28707,7 @@ package android.os {
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -49260,9 +49261,7 @@ package java.io {
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -52776,9 +52775,7 @@ package java.net {
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53860,9 +53857,7 @@ package java.security {
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53876,9 +53871,7 @@ package java.security {
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54256,10 +54249,8 @@ package java.security {
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -54517,13 +54508,11 @@ package java.security {
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54581,8 +54570,6 @@ package java.security.acl {
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -63891,11 +63878,9 @@ package javax.security.auth {
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/removed.txt b/api/removed.txt
index 86085c8ded67..3f16bca3eff1 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -94,6 +94,10 @@ package android.media {
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 087b25065f2f..008b8603afca 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24638,7 +24638,7 @@ package android.media.tv {
}
public final class TvInputManager {
- method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputInfo, android.media.tv.TvInputManager.HardwareCallback);
method public void addBlockedRating(android.media.tv.TvContentRating);
method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
@@ -30951,6 +30951,7 @@ package android.os {
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -52351,9 +52352,7 @@ package java.io {
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -55867,9 +55866,7 @@ package java.net {
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -56951,9 +56948,7 @@ package java.security {
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -56967,9 +56962,7 @@ package java.security {
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -57347,10 +57340,8 @@ package java.security {
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -57608,13 +57599,11 @@ package java.security {
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -57672,8 +57661,6 @@ package java.security.acl {
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -66982,11 +66969,9 @@ package javax.security.auth {
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index bc1762722c18..03cf8b0902a4 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -92,6 +92,10 @@ package android.media {
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index fac2219f7196..78a1d3af422a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -28772,6 +28772,7 @@ package android.os {
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -49334,9 +49335,7 @@ package java.io {
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -52850,9 +52849,7 @@ package java.net {
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53934,9 +53931,7 @@ package java.security {
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53950,9 +53945,7 @@ package java.security {
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54330,10 +54323,8 @@ package java.security {
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -54591,13 +54582,11 @@ package java.security {
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54655,8 +54644,6 @@ package java.security.acl {
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -63965,11 +63952,9 @@ package javax.security.auth {
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 86085c8ded67..3f16bca3eff1 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -94,6 +94,10 @@ package android.media {
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index baaa55d34841..d1f514327731 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -377,6 +377,12 @@ public class ActivityManager {
/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;
+ /** @hide The lowest process state number */
+ public static final int MIN_PROCESS_STATE = PROCESS_STATE_NONEXISTENT;
+
+ /** @hide The highest process state number */
+ public static final int MAX_PROCESS_STATE = PROCESS_STATE_CACHED_EMPTY;
+
/** @hide Should this process state be considered a background state? */
public static final boolean isProcStateBackground(int procState) {
return procState >= PROCESS_STATE_BACKUP;
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index a5617b46b403..40e1a9f8ba79 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -103,53 +103,22 @@ public class LauncherActivityInfo {
* @return The drawable associated with the activity.
*/
public Drawable getIcon(int density) {
- final int iconRes = mResolveInfo.getIconResource();
- Drawable icon = getDrawableForDensity(iconRes, density);
- // Get the default density icon
- if (icon == null) {
- icon = mResolveInfo.loadIcon(mPm);
- }
- return icon;
- }
-
- /**
- * Returns the icon for this activity, without any badging for the profile.
- * This function can get the icon no matter the icon needs to be badged or not.
- * @param density The preferred density of the icon, zero for default density. Use
- * density DPI values from {@link DisplayMetrics}.
- * @see #getBadgedIcon(int)
- * @see DisplayMetrics
- * @return The drawable associated with the activity.
- */
- private Drawable getOriginalIcon(int density) {
final int iconRes = mResolveInfo.getIconResourceInternal();
- Drawable icon = getDrawableForDensity(iconRes, density);
- // Get the default density icon
- if (icon == null) {
- icon = mResolveInfo.loadIcon(mPm);
- }
- return icon;
- }
-
- /**
- * Returns the drawable for this activity, without any badging for the profile.
- * @param iconRes id of the drawable.
- * @param density The preferred density of the icon, zero for default density. Use
- * density DPI values from {@link DisplayMetrics}.
- * @see DisplayMetrics
- * @return The drawable associated with the resource id.
- */
- private Drawable getDrawableForDensity(int iconRes, int density) {
+ Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
try {
final Resources resources
= mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
- return resources.getDrawableForDensity(iconRes, density);
+ icon = resources.getDrawableForDensity(iconRes, density);
} catch (NameNotFoundException | Resources.NotFoundException exc) {
}
}
- return null;
+ // Get the default density icon
+ if (icon == null) {
+ icon = mResolveInfo.loadIcon(mPm);
+ }
+ return icon;
}
/**
@@ -201,7 +170,7 @@ public class LauncherActivityInfo {
* @return A badged icon for the activity.
*/
public Drawable getBadgedIcon(int density) {
- Drawable originalIcon = getOriginalIcon(density);
+ Drawable originalIcon = getIcon(density);
if (originalIcon instanceof BitmapDrawable) {
return mPm.getUserBadgedIcon(originalIcon, mUser);
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index f13e5b57cbd0..9d362d61d9d4 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -48,7 +48,8 @@ public class HardwarePropertiesManager {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN
+ TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN,
+ TEMPERATURE_THROTTLING_BELOW_VR_MIN
})
public @interface TemperatureSource {}
@@ -77,6 +78,12 @@ public class HardwarePropertiesManager {
/** Get shutdown temperature threshold. */
public static final int TEMPERATURE_SHUTDOWN = 2;
+ /**
+ * Get throttling temperature threshold above which minimum clockrates for VR mode will not be
+ * met.
+ */
+ public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3;
+
/** Undefined temperature constant. */
public static final float UNDEFINED_TEMPERATURE = -Float.MAX_VALUE;
@@ -96,7 +103,8 @@ public class HardwarePropertiesManager {
* {@link #DEVICE_TEMPERATURE_GPU}, {@link #DEVICE_TEMPERATURE_BATTERY} or {@link
* #DEVICE_TEMPERATURE_SKIN}.
* @param source source of requested device temperature, one of {@link #TEMPERATURE_CURRENT},
- * {@link #TEMPERATURE_THROTTLING} or {@link #TEMPERATURE_SHUTDOWN}.
+ * {@link #TEMPERATURE_THROTTLING}, {@link #TEMPERATURE_THROTTLING_BELOW_VR_MIN} or
+ * {@link #TEMPERATURE_SHUTDOWN}.
* @return an array of requested float device temperatures. Temperature equals to
* {@link #UNDEFINED_TEMPERATURE} if undefined.
* Empty if platform doesn't provide the queried temperature.
@@ -115,6 +123,7 @@ public class HardwarePropertiesManager {
case TEMPERATURE_CURRENT:
case TEMPERATURE_THROTTLING:
case TEMPERATURE_SHUTDOWN:
+ case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
try {
return mService.getDeviceTemperatures(mContext.getOpPackageName(), type,
source);
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 75ccf355ecd7..d106dba4dd3a 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -16,32 +16,16 @@
package android.webkit;
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.AndroidRuntimeException;
-import android.util.Base64;
import java.util.Arrays;
/** @hide */
-public class WebViewProviderInfo implements Parcelable {
+public final class WebViewProviderInfo implements Parcelable {
- /**
- * @hide
- */
- public static class WebViewPackageNotFoundException extends AndroidRuntimeException {
- public WebViewPackageNotFoundException(String message) { super(message); }
- public WebViewPackageNotFoundException(Exception e) { super(e); }
- }
-
- public WebViewProviderInfo(String packageName, String description, boolean availableByDefault,
- boolean isFallback, String[] signatures) {
+ public WebViewProviderInfo(String packageName, String description,
+ boolean availableByDefault, boolean isFallback, String[] signatures) {
this.packageName = packageName;
this.description = description;
this.availableByDefault = availableByDefault;
@@ -49,92 +33,6 @@ public class WebViewProviderInfo implements Parcelable {
this.signatures = signatures;
}
- private boolean hasValidSignature() {
- if (Build.IS_DEBUGGABLE)
- return true;
- Signature[] packageSignatures;
- try {
- // If no signature is declared, instead check whether the package is included in the
- // system.
- if (signatures == null || signatures.length == 0)
- return getPackageInfo().applicationInfo.isSystemApp();
-
- packageSignatures = getPackageInfo().signatures;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- if (packageSignatures.length != 1)
- return false;
-
- final byte[] packageSignature = packageSignatures[0].toByteArray();
- // Return whether the package signature matches any of the valid signatures
- for (String signature : signatures) {
- final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
- if (Arrays.equals(packageSignature, validSignature))
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether this provider is valid for use as a WebView provider.
- */
- public boolean isValidProvider() {
- ApplicationInfo applicationInfo;
- try {
- applicationInfo = getPackageInfo().applicationInfo;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- if (hasValidSignature() && WebViewFactory.getWebViewLibrary(applicationInfo) != null) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether this package is enabled.
- * This state can be changed by the user from Settings->Apps
- */
- public boolean isEnabled() {
- try {
- // Explicitly fetch up-to-date package info here since the enabled-state of the package
- // might have changed since we last fetched its package info.
- updatePackageInfo();
- return getPackageInfo().applicationInfo.enabled;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- }
-
- /**
- * Returns whether the provider is always available as long as it is valid.
- * If this returns false, the provider will only be used if the user chose this provider.
- */
- public boolean isAvailableByDefault() {
- return availableByDefault;
- }
-
- public boolean isFallbackPackage() {
- return isFallback;
- }
-
- private void updatePackageInfo() {
- try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
- } catch (PackageManager.NameNotFoundException e) {
- throw new WebViewPackageNotFoundException(e);
- }
- }
-
- public PackageInfo getPackageInfo() {
- if (packageInfo == null) {
- updatePackageInfo();
- }
- return packageInfo;
- }
-
// aidl stuff
public static final Parcelable.Creator<WebViewProviderInfo> CREATOR =
new Parcelable.Creator<WebViewProviderInfo>() {
@@ -153,7 +51,6 @@ public class WebViewProviderInfo implements Parcelable {
availableByDefault = (in.readInt() > 0);
isFallback = (in.readInt() > 0);
signatures = in.createStringArray();
- packageInfo = null;
}
@Override
@@ -171,16 +68,9 @@ public class WebViewProviderInfo implements Parcelable {
}
// fields read from framework resource
- public String packageName;
- public String description;
- private boolean availableByDefault;
- private boolean isFallback;
-
- private String[] signatures;
-
- private PackageInfo packageInfo;
-
- // flags declaring we want extra info from the package manager
- private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+ public final String packageName;
+ public final String description;
+ public final boolean availableByDefault;
+ public final boolean isFallback;
+ public final String[] signatures;
}
diff --git a/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java b/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java
new file mode 100644
index 000000000000..a6e433fcb8d8
--- /dev/null
+++ b/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java
@@ -0,0 +1,89 @@
+/*
+ * 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.text.util;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.util.Patterns;
+
+import java.util.regex.Pattern;
+
+public class LinkifyBenchmark {
+ private static final String MATCHING_STR = " http://user:pass@host.com:5432/path?k=v#f " +
+ "host.com:5432/path?k=v#f ";
+
+ private static final String NONMATCHING_STR = " Neque porro quisquam est qui dolorem ipsum " +
+ "quia dolor sit amet, consectetur, adipisci velit ";
+
+ // this pattern does not recognize strings without http scheme therefore is expected to be
+ // faster in MATCHING_STR case.
+ private static final Pattern BASIC_PATTERN = Pattern.compile(
+ "(?:\\b|$|^)http://[a-zA-Z0-9:\\.@\\?=#/]+(?:\\b|$|^)");
+
+ @Param({"1", "4", "16", "64", "256"})
+ private String mParamCopyAmount;
+
+ @Param({MATCHING_STR, NONMATCHING_STR})
+ private String mParamBasicText;
+
+ private Spannable mTestSpannable;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ int copyAmount = Integer.valueOf(mParamCopyAmount);
+ StringBuilder strBuilder = new StringBuilder();
+ for (int i = 0; i < copyAmount; i++) {
+ strBuilder.append(mParamBasicText);
+ }
+ mTestSpannable = new SpannableString(strBuilder.toString());
+ }
+
+ @AfterExperiment
+ protected void tearDown() {
+ mTestSpannable = null;
+ }
+
+ @Benchmark
+ public void timeNewRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, Patterns.AUTOLINK_WEB_URL, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+ @Benchmark
+ public void timeOldRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, Patterns.WEB_URL, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+ @Benchmark
+ public void timeBasicRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, BASIC_PATTERN, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+}
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index ea4f4eb98f36..f43bf860946b 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -195,7 +195,7 @@ void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
}
static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
- const TextOp& op, const BakedOpState& state) {
+ const TextOp& op, const BakedOpState& textOpState) {
renderer.caches().textureState().activateTexture(0);
PaintUtils::TextShadow textShadow;
@@ -216,13 +216,41 @@ static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRender
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
+ .setRoundRectClipState(textOpState.roundRectClipState)
.setMeshTexturedUnitQuad(nullptr)
- .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
+ .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha)
+ .setTransform(textOpState.computedState.transform, TransformFlags::None)
.setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
.build();
- renderer.renderGlop(state, glop);
+
+ // Compute damage bounds and clip (since may differ from those in textOpState).
+ // Bounds should be same as text op, but with dx/dy offset and radius outset
+ // applied in local space.
+ auto& transform = textOpState.computedState.transform;
+ Rect shadowBounds = op.unmappedBounds; // STROKE
+ const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style;
+ if (expandForStroke) {
+ shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f);
+ }
+ shadowBounds.translate(textShadow.dx, textShadow.dy);
+ shadowBounds.outset(textShadow.radius, textShadow.radius);
+ transform.mapRect(shadowBounds);
+ if (CC_UNLIKELY(expandForStroke &&
+ (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) {
+ shadowBounds.outset(0.5f);
+ }
+
+ auto clipState = textOpState.computedState.clipState;
+ if (clipState->mode != ClipMode::Rectangle
+ || !clipState->rect.contains(shadowBounds)) {
+ // need clip, so pass it and clip bounds
+ shadowBounds.doIntersect(clipState->rect);
+ } else {
+ // don't need clip, ignore
+ clipState = nullptr;
+ }
+
+ renderer.renderGlop(&shadowBounds, clipState, glop);
}
enum class TextRenderType {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 704bd69056fe..6a9663412a00 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -163,11 +163,13 @@ public:
GLenum dst;
} blend;
+#if !HWUI_NEW_OPS
/**
* Bounds of the drawing command in layer space. Only mapped into layer
* space once GlopBuilder::build() is called.
*/
Rect bounds; // TODO: remove for HWUI_NEW_OPS
+#endif
/**
* Additional render state to enumerate:
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2799def16b98..7d4f4100313f 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -492,7 +492,9 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRect(const Rect destination) {
mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = destination;
+#endif
return *this;
}
@@ -516,7 +518,9 @@ GlopBuilder& GlopBuilder::setModelViewMapUnitToRectSnap(const Rect destination)
mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = destination;
+#endif
return *this;
}
@@ -524,8 +528,10 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRect(float offsetX, float offsetY, c
TRIGGER_STAGE(kModelViewStage);
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = source;
mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
return *this;
}
@@ -545,8 +551,10 @@ GlopBuilder& GlopBuilder::setModelViewOffsetRectSnap(float offsetX, float offset
}
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = source;
mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
return *this;
}
@@ -643,7 +651,9 @@ void GlopBuilder::build() {
// Final step: populate program and map bounds into render target space
mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+#if !HWUI_NEW_OPS
mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
+#endif
}
void GlopBuilder::dump(const Glop& glop) {
@@ -683,7 +693,9 @@ void GlopBuilder::dump(const Glop& glop) {
ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
+#if !HWUI_NEW_OPS
ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
+#endif
}
} /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 47d01088f6f7..d1ff67027d07 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -56,24 +56,17 @@ static const Comparison COMPARISONS[] = {
static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
/*
- * Frames that are exempt from jank metrics.
- * First-draw frames, for example, are expected to
- * be slow, this is hidden from the user with window animations and
- * other tricks
- *
- * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
+ * We don't track direct-drawing via Surface:lockHardwareCanvas()
* for now
*
* TODO: kSurfaceCanvas can negatively impact other drawing by using up
* time on the RenderThread, figure out how to attribute that as a jank-causer
*/
-static const int64_t EXEMPT_FRAMES_FLAGS
- = FrameInfoFlags::WindowLayoutChanged
- | FrameInfoFlags::SurfaceCanvas;
+static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
// The bucketing algorithm controls so to speak
// If a frame is <= to this it goes in bucket 0
-static const uint32_t kBucketMinThreshold = 7;
+static const uint32_t kBucketMinThreshold = 5;
// If a frame is > this, start counting in increments of 2ms
static const uint32_t kBucket2msIntervals = 32;
// If a frame is > this, start counting in increments of 4ms
@@ -84,9 +77,14 @@ static const uint32_t kBucket4msIntervals = 48;
// and filter it out of the frame profile data
static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
+// The interval of the slow frame histogram
+static const uint32_t kSlowFrameBucketIntervalMs = 50;
+// The start point of the slow frame bucket in ms
+static const uint32_t kSlowFrameBucketStartMs = 150;
+
// This will be called every frame, performance sensitive
// Uses bit twiddling to avoid branching while achieving the packing desired
-static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) {
uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
// If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
// of negating 1 (twos compliment, yaay) else mask will be 0
@@ -104,7 +102,7 @@ static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
// be a pretty garbage value right now. However, mask is 0 so we'll end
// up with the desired result of 0.
index = (index - kBucketMinThreshold) & mask;
- return index < max ? index : max;
+ return index;
}
// Only called when dumping stats, less performance sensitive
@@ -211,63 +209,34 @@ void JankTracker::setFrameInterval(nsecs_t frameInterval) {
}
-static bool shouldReplace(SlowFrame& existing, SlowFrame& candidate) {
- if (candidate.whenHours - existing.whenHours >= 24) {
- // If the old slowframe is over 24 hours older than the candidate,
- // replace it. It's too stale
- return true;
- }
- if (candidate.frametimeMs > existing.frametimeMs) {
- return true;
- }
- return false;
-}
-
-void JankTracker::updateSlowest(const FrameInfo& frame) {
- uint16_t durationMs = static_cast<uint16_t>(std::min(
- ns2ms(frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync]),
- static_cast<nsecs_t>(std::numeric_limits<uint16_t>::max())));
- uint16_t startHours = static_cast<uint16_t>(std::lround(
- ns2s(frame[FrameInfoIndex::IntendedVsync]) / 3600.0f));
- SlowFrame* toReplace = nullptr;
- SlowFrame thisFrame{startHours, durationMs};
- // First find the best candidate for replacement
- for (SlowFrame& existing : mData->slowestFrames) {
- // If we should replace the current data with the replacement candidate,
- // it means the current data is worse than the replacement candidate
- if (!toReplace || shouldReplace(existing, *toReplace)) {
- toReplace = &existing;
- }
- }
- // Now see if we should replace it
- if (shouldReplace(*toReplace, thisFrame)) {
- *toReplace = thisFrame;
- }
-}
-
void JankTracker::addFrame(const FrameInfo& frame) {
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration =
frame[FrameInfoIndex::FrameCompleted] - frame[sFrameStart];
- uint32_t framebucket = frameCountIndexForFrameTime(
- totalDuration, mData->frameCounts.size() - 1);
+ uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
mData->frameCounts[framebucket]++;
return;
}
- // For slowest frames we are still interested in frames that are otherwise
- // exempt (such as first-draw). Although those frames don't directly impact
- // smoothness, they do impact responsiveness.
- updateSlowest(frame);
-
+ // Only things like Surface.lockHardwareCanvas() are exempt from tracking
if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
return;
}
- mData->frameCounts[framebucket]++;
+ if (framebucket <= mData->frameCounts.size()) {
+ mData->frameCounts[framebucket]++;
+ } else {
+ framebucket = (ns2ms(totalDuration) - kSlowFrameBucketStartMs)
+ / kSlowFrameBucketIntervalMs;
+ framebucket = std::min(framebucket,
+ static_cast<uint32_t>(mData->slowFrameCounts.size() - 1));
+ framebucket = std::max(framebucket, 0u);
+ mData->slowFrameCounts[framebucket]++;
+ }
+
mData->jankFrameCount++;
for (int i = 0; i < NUM_BUCKETS; i++) {
@@ -298,14 +267,18 @@ void JankTracker::dumpData(const ProfileData* data, int fd) {
dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
- dprintf(fd, "\nSlowest frames over last 24h: ");
- for (auto& slowFrame : data->slowestFrames) {
- if (!slowFrame.frametimeMs) continue;
- dprintf(fd, "%ums ", slowFrame.frametimeMs);
- }
for (int i = 0; i < NUM_BUCKETS; i++) {
dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
}
+ dprintf(fd, "\nHISTOGRAM:");
+ for (size_t i = 0; i < data->frameCounts.size(); i++) {
+ dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i),
+ data->frameCounts[i]);
+ }
+ for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
+ dprintf(fd, " %zums=%u", (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs,
+ data->slowFrameCounts[i]);
+ }
dprintf(fd, "\n");
}
@@ -323,6 +296,12 @@ void JankTracker::reset() {
uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
int pos = percentile * data->totalFrameCount / 100;
int remaining = data->totalFrameCount - pos;
+ for (int i = data->slowFrameCounts.size() - 1; i >= 0; i--) {
+ remaining -= data->slowFrameCounts[i];
+ if (remaining <= 0) {
+ return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
+ }
+ }
for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
remaining -= data->frameCounts[i];
if (remaining <= 0) {
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 1a4a4897f496..84b8c3f3f155 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -39,23 +39,18 @@ enum JankType {
NUM_BUCKETS,
};
-struct SlowFrame {
- uint16_t whenHours; // When this occurred in CLOCK_MONOTONIC in hours
- uint16_t frametimeMs; // How long the frame took in ms
-};
-
// Try to keep as small as possible, should match ASHMEM_SIZE in
// GraphicsStatsService.java
struct ProfileData {
std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
// See comments on kBucket* constants for what this holds
- std::array<uint32_t, 55> frameCounts;
+ std::array<uint32_t, 57> frameCounts;
+ // Holds a histogram of frame times in 50ms increments from 150ms to 5s
+ std::array<uint16_t, 97> slowFrameCounts;
uint32_t totalFrameCount;
uint32_t jankFrameCount;
nsecs_t statStartTime;
-
- std::array<SlowFrame, 10> slowestFrames;
};
// TODO: Replace DrawProfiler with this
@@ -78,7 +73,6 @@ public:
private:
void freeData();
void setFrameInterval(nsecs_t frameIntervalNanos);
- void updateSlowest(const FrameInfo& frame);
static uint32_t findPercentile(const ProfileData* data, int p);
static void dumpData(const ProfileData* data, int fd);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index c0994272c964..53ea7fa6f77d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1413,7 +1413,9 @@ void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
// TODO: specify more clearly when a draw should dirty the layer.
// is writing to the stencil the only time we should ignore this?
+#if !HWUI_NEW_OPS
dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+#endif
mDirty = true;
}
}
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 04223a7d5188..16dd108488b2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -56,6 +56,7 @@ namespace DumpFlags {
enum {
FrameStats = 1 << 0,
Reset = 1 << 1,
+ JankStats = 1 << 2,
};
};
@@ -415,7 +416,6 @@ void RenderProxy::notifyFramePending() {
CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
int fd, int dumpFlags) {
args->context->profiler().dumpData(args->fd);
- args->thread->jankTracker().dump(args->fd);
if (args->dumpFlags & DumpFlags::FrameStats) {
args->context->dumpFrames(args->fd);
}
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 654ddc6a28e5..54714860c19d 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -85,7 +85,9 @@ RENDERTHREAD_TEST(BakedOpDispatcher, onArc_position) {
<< "Should see conservative offset from PathCache::computeBounds";
Rect expectedBounds(10, 15, 20, 25);
expectedBounds.outset(expectedOffset);
+#if !HWUI_NEW_OPS
EXPECT_EQ(expectedBounds, glop.bounds) << "bounds outset by stroke 'offset'";
+#endif
Matrix4 expectedModelView;
expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index 454011fa47f0..95543d33b1ef 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -85,7 +85,9 @@ static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transfor
}
static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
+#if !HWUI_NEW_OPS
EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
+#endif
expectBlendEq(expectedGlop.blend, builtGlop.blend);
expectFillEq(expectedGlop.fill, builtGlop.fill);
expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
@@ -136,7 +138,9 @@ RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
// unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
goldenGlop->transform.modelView.scale(99, 99, 1);
+#if !HWUI_NEW_OPS
goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
+#endif
goldenGlop->transform.canvas = simpleTranslate;
goldenGlop->fill.texture.filter = GL_NEAREST;
expectGlopEq(*goldenGlop, glop);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c72a7a027e90..04ea683b505f 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -56,7 +56,26 @@ import java.util.Map;
/**
* Central system API to the overall TV input framework (TIF) architecture, which arbitrates
- * interaction between applications and the selected TV inputs.
+ * interaction between applications and the selected TV inputs. You can retrieve an instance of
+ * this interface with {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TV_INPUT_SERVICE)}.
+ *
+ * <p>There are three primary parties involved in the TV input framework (TIF) architecture:
+ *
+ * <ul>
+ * <li>The <strong>TV input manager</strong> as expressed by this class is the central point of the
+ * system that manages interaction between all other parts. It is expressed as the client-side API
+ * here which exists in each application context and communicates with a global system service that
+ * manages the interaction across all processes.
+ * <li>A <strong>TV input</strong> implemented by {@link TvInputService} represents an input source
+ * of TV, which can be a pass-through input such as HDMI, or a tuner input which provides broadcast
+ * TV programs. The system binds to the TV input per application’s request.
+ * on implementing TV inputs.
+ * <li><strong>Applications</strong> talk to the TV input manager to list TV inputs and check their
+ * status. Once an application find the input to use, it uses {@link TvView} or
+ * {@link TvRecordingClient} for further interaction such as watching and recording broadcast TV
+ * programs.
+ * </ul>
*/
public final class TvInputManager {
private static final String TAG = "TvInputManager";
@@ -824,11 +843,21 @@ public final class TvInputManager {
/**
* Interface used to receive events from Hardware objects.
+ *
* @hide
*/
@SystemApi
public abstract static class HardwareCallback {
+ /**
+ * This is called when {@link Hardware} is no longer available for the client.
+ */
public abstract void onReleased();
+
+ /**
+ * This is called when the underlying {@link TvStreamConfig} has been changed.
+ *
+ * @param configs A list of new {@link TvStreamConfig}s.
+ */
public abstract void onStreamConfigChanged(TvStreamConfig[] configs);
}
@@ -1470,18 +1499,41 @@ public final class TvInputManager {
}
/**
- * Returns acquired TvInputManager.Hardware object for given deviceId.
+ * Acquires {@link Hardware} object for the given device ID.
*
- * If there are other Hardware object acquired for the same deviceId, calling this method will
- * preempt the previously acquired object and report {@link HardwareCallback#onReleased} to the
- * old object.
+ * <p>A subsequent call to this method on the same {@code deviceId} will release the currently
+ * acquired Hardware.
*
- * @hide
+ * @param deviceId The device ID to acquire Hardware for.
+ * @param callback A callback to receive updates on Hardware.
+ * @param info The TV input which will use the acquired Hardware.
+ * @return Hardware on success, {@code null} otherwise.
+ *
+ * @removed
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
TvInputInfo info) {
+ return acquireTvInputHardware(deviceId, info, callback);
+ }
+
+ /**
+ * Acquires {@link Hardware} object for the given device ID.
+ *
+ * <p>A subsequent call to this method on the same {@code deviceId} will release the currently
+ * acquired Hardware.
+ *
+ * @param deviceId The device ID to acquire Hardware for.
+ * @param callback A callback to receive updates on Hardware.
+ * @param info The TV input which will use the acquired Hardware.
+ * @return Hardware on success, {@code null} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+ public Hardware acquireTvInputHardware(int deviceId, TvInputInfo info,
+ final HardwareCallback callback) {
try {
return new Hardware(
mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() {
@@ -1503,6 +1555,9 @@ public final class TvInputManager {
/**
* Releases previously acquired hardware object.
*
+ * @param deviceId The device ID this Hardware was acquired for
+ * @param hardware Hardware to release.
+ *
* @hide
*/
@SystemApi
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
new file mode 100644
index 000000000000..4d2a35e508a9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
@@ -0,0 +1,45 @@
+<!--
+ 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="26.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0 0h26v24H0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21.0,8.5
+ c0.85,0.0 1.6,0.23 2.3,0.62l2.24,-2.79
+ C25.1,5.96 20.26,2.0 13.0,2.0
+ S0.9,5.9 0.42,6.32
+ l12.57,15.6 4.21,-5.17
+ c-0.76,-0.87 -1.22,-2.0 -1.22,-3.25
+ c0.0,-2.76 2.24,-5.0 5.0,-5.0z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21.0,10.0
+ c-1.93,0.0 -3.5,1.57 -3.5,3.5l1.75,0.0
+ c0.0,-0.9 0.78,-1.75 1.75,-1.75s1.7,0.78 1.75,1.75
+ c0.0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1
+ c-0.6,0.63 -1.02,1.51 -1.02,2.47l0.0,0.44l1.75,0.0
+ c0.0,-1.3 0.39,-1.84 1.03,-2.47l0.78,-0.8
+ c0.5,-0.5 0.82,-1.2 0.82,-1.97
+ C24.5,11.57 22.93,10.0 21.0,10.0z
+ m-0.95,11.95l1.9,0.0l0.0,-1.9l-1.9,0.0l0.0,1.9z"/>
+</vector>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 83ac45c5c8c6..716185f343b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -125,6 +125,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
mNotifQsContainer.setCustomizerAnimating(true);
+ mNotifQsContainer.setCustomizerShowing(true);
}
}
@@ -137,6 +138,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
save();
mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
mNotifQsContainer.setCustomizerAnimating(true);
+ mNotifQsContainer.setCustomizerShowing(false);
}
}
@@ -175,6 +177,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
specs.add(tile.getTileSpec());
}
mTileAdapter.setTileSpecs(specs);
+ mRecyclerView.setAdapter(mTileAdapter);
}
private void save() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 6ddd46ec134e..203278300914 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Looper;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
@@ -44,7 +45,6 @@ import java.text.NumberFormat;
public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback {
- private final BatteryMeterDrawable mDrawable;
private final BatteryController mBatteryController;
private final BatteryDetail mBatteryDetail = new BatteryDetail();
@@ -52,13 +52,11 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
private boolean mPowerSave;
private boolean mCharging;
private boolean mDetailShown;
+ private boolean mPluggedIn;
public BatteryTile(Host host) {
super(host);
mBatteryController = host.getBatteryController();
- mDrawable = new BatteryMeterDrawable(host.getContext(), new Handler(),
- host.getContext().getColor(R.color.batterymeter_frame_color));
- mDrawable.setBatteryController(mBatteryController);
}
@Override
@@ -79,10 +77,8 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
@Override
public void setListening(boolean listening) {
if (listening) {
- mDrawable.startListening();
mBatteryController.addStateChangedCallback(this);
} else {
- mDrawable.stopListening();
mBatteryController.removeStateChangedCallback(this);
}
}
@@ -118,7 +114,12 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
state.icon = new Icon() {
@Override
public Drawable getDrawable(Context context) {
- return mDrawable;
+ BatteryMeterDrawable drawable =
+ new BatteryMeterDrawable(context, new Handler(Looper.getMainLooper()),
+ context.getColor(R.color.batterymeter_frame_color));
+ drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ drawable.onPowerSaveChanged(mPowerSave);
+ return drawable;
}
@Override
@@ -133,6 +134,7 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mLevel = level;
+ mPluggedIn = pluggedIn;
mCharging = charging;
refreshState((Integer) level);
if (mDetailShown) {
@@ -143,6 +145,7 @@ public class BatteryTile extends QSTile<QSTile.State> implements BatteryControll
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
mPowerSave = isPowerSave;
+ refreshState(null);
if (mDetailShown) {
mBatteryDetail.postBindView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 65154f2bad34..c72bbf394beb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -154,7 +154,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> {
state.label = removeDoubleQuotes(cb.enabledDesc);
signalContentDescription = cb.wifiSignalContentDescription;
} else if (wifiNotConnected) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_full_0);
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
state.label = r.getString(R.string.quick_settings_wifi_label);
signalContentDescription = r.getString(R.string.accessibility_no_wifi);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 960515b9137a..35fb2a50306e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -35,6 +35,7 @@ import com.android.systemui.qs.customize.QSCustomizer;
public class NotificationsQuickSettingsContainer extends FrameLayout
implements ViewStub.OnInflateListener, DensityContainer.InflateListener {
+
private DensityContainer mQsContainer;
private View mUserSwitcher;
private View mStackScroller;
@@ -43,6 +44,9 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
+ private int mBottomPadding;
+ private int mStackScrollerMargin;
+
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -53,6 +57,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
mQsContainer = (DensityContainer) findViewById(R.id.qs_density_container);
mQsContainer.addInflateListener(this);
mStackScroller = findViewById(R.id.notification_stack_scroller);
+ mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
userSwitcher.setOnInflateListener(this);
@@ -75,7 +80,8 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- setPadding(0, 0, 0, insets.getStableInsetBottom());
+ mBottomPadding = insets.getStableInsetBottom();
+ setPadding(0, 0, 0, mBottomPadding);
return insets;
}
@@ -141,4 +147,22 @@ public class NotificationsQuickSettingsContainer extends FrameLayout
invalidate();
}
}
+
+ public void setCustomizerShowing(boolean isShowing) {
+ if (isShowing) {
+ // Clear out bottom paddings/margins so the qs customization can be full height.
+ setPadding(0, 0, 0, 0);
+ setBottomMargin(mStackScroller, 0);
+ } else {
+ setPadding(0, 0, 0, mBottomPadding);
+ setBottomMargin(mStackScroller, mStackScrollerMargin);
+ }
+
+ }
+
+ private void setBottomMargin(View v, int bottomMargin) {
+ LayoutParams params = (LayoutParams) v.getLayoutParams();
+ params.bottomMargin = bottomMargin;
+ v.setLayoutParams(params);
+ }
}
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index e29515fc36ae..ecbe1ca98911 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -51,7 +51,7 @@ import java.util.ArrayList;
* 2) ASHMEM_SIZE (for scratch space used during dumping)
* 3) ASHMEM_SIZE * HISTORY_SIZE
*
- * This is currently under 16KiB total memory in the worst case of
+ * This is currently under 20KiB total memory in the worst case of
* 20 processes in history + 10 unique active processes.
*
* @hide */
@@ -59,7 +59,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
private static final String TAG = "GraphicsStatsService";
- private static final int ASHMEM_SIZE = 296;
+ private static final int ASHMEM_SIZE = 464;
private static final int HISTORY_SIZE = 20;
private final Context mContext;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1632f92dd40c..bb323031c011 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -65,6 +65,7 @@ import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -75,11 +76,13 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
@@ -91,6 +94,7 @@ import com.google.android.collect.Sets;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
@@ -107,7 +111,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -126,7 +129,9 @@ public class AccountManagerService
private static final String TAG = "AccountManagerService";
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 9;
+ private static final int PRE_N_DATABASE_VERSION = 9;
+ private static final int CE_DATABASE_VERSION = 10;
+ private static final int DE_DATABASE_VERSION = 1;
private static final int MAX_DEBUG_DB_SIZE = 64;
@@ -176,6 +181,15 @@ public class AccountManagerService
private static final String META_VALUE = "value";
private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
+ private static final String SHARED_ACCOUNTS_ID = "_id";
+
+ private static final String PRE_N_DATABASE_NAME = "accounts.db";
+ private static final String CE_DATABASE_NAME = "accounts_ce.db";
+ private static final String DE_DATABASE_NAME = "accounts_de.db";
+ private static final String CE_DB_PREFIX = "ceDb.";
+ private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
+ private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
+ private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
@@ -214,7 +228,7 @@ public class AccountManagerService
static class UserAccounts {
private final int userId;
- private final DatabaseHelper openHelper;
+ private final DeDatabaseHelper openHelper;
private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
credentialsPermissionNotificationIds =
new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
@@ -255,15 +269,15 @@ public class AccountManagerService
UserAccounts(Context context, int userId) {
this.userId = userId;
synchronized (cacheLock) {
- openHelper = new DatabaseHelper(context, userId);
+ openHelper = DeDatabaseHelper.create(context, userId);
}
}
}
- private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
+ private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
+ private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
- private static AtomicReference<AccountManagerService> sThis =
- new AtomicReference<AccountManagerService>();
+ private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
/**
@@ -381,6 +395,11 @@ public class AccountManagerService
*/
private void validateAccountsInternal(
UserAccounts accounts, boolean invalidateAuthenticatorCache) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "validateAccountsInternal " + accounts.userId
+ + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
+ + " userLocked=" + mUnlockedUsers.get(accounts.userId));
+ }
if (invalidateAuthenticatorCache) {
mAuthenticatorCache.invalidateCache(accounts.userId);
}
@@ -453,8 +472,7 @@ public class AccountManagerService
null, null, null, null, ACCOUNTS_ID);
try {
accounts.accountCache.clear();
- final HashMap<String, ArrayList<String>> accountNamesByType =
- new LinkedHashMap<String, ArrayList<String>>();
+ final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
while (cursor.moveToNext()) {
final long accountId = cursor.getLong(0);
final String accountType = cursor.getString(1);
@@ -482,15 +500,12 @@ public class AccountManagerService
accountNames.add(accountName);
}
}
- for (Map.Entry<String, ArrayList<String>> cur
- : accountNamesByType.entrySet()) {
+ for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
final String accountType = cur.getKey();
final ArrayList<String> accountNames = cur.getValue();
final Account[] accountsForType = new Account[accountNames.size()];
- int i = 0;
- for (String accountName : accountNames) {
- accountsForType[i] = new Account(accountName, accountType);
- ++i;
+ for (int i = 0; i < accountsForType.length; i++) {
+ accountsForType[i] = new Account(accountNames.get(i), accountType);
}
accounts.accountCache.put(accountType, accountsForType);
}
@@ -522,12 +537,25 @@ public class AccountManagerService
protected UserAccounts getUserAccounts(int userId) {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
+ boolean validateAccounts = false;
if (accounts == null) {
accounts = new UserAccounts(mContext, userId);
initializeDebugDbSizeAndCompileSqlStatementForLogging(
accounts.openHelper.getWritableDatabase(), accounts);
mUsers.append(userId, accounts);
purgeOldGrants(accounts);
+ validateAccounts = true;
+ }
+ // open CE database if necessary
+ if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
+ Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
+ synchronized (accounts.cacheLock) {
+ CeDatabaseHelper.create(mContext, userId);
+ accounts.openHelper.attachCeDatabase();
+ }
+ // TODO Synchronize accounts by removing CE account not available in DE
+ }
+ if (validateAccounts) {
validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
}
return accounts;
@@ -571,27 +599,51 @@ public class AccountManagerService
if (userId < 1) return;
UserAccounts accounts;
+ boolean userUnlocked;
synchronized (mUsers) {
accounts = mUsers.get(userId);
mUsers.remove(userId);
+ userUnlocked = mUnlockedUsers.get(userId);
+ mUnlockedUsers.delete(userId);
}
- if (accounts == null) {
- File dbFile = new File(getDatabaseName(userId));
- dbFile.delete();
- return;
+ if (accounts != null) {
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ }
}
+ Log.i(TAG, "Removing database files for user " + userId);
+ File dbFile = new File(getDeDatabaseName(userId));
- synchronized (accounts.cacheLock) {
- accounts.openHelper.close();
- File dbFile = new File(getDatabaseName(userId));
- dbFile.delete();
+ deleteDbFileWarnIfFailed(dbFile);
+ // Remove CE file if user is unlocked, or FBE is not enabled
+ boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
+ if (!fbeEnabled || userUnlocked) {
+ File ceDb = new File(getCeDatabaseName(userId));
+ if (ceDb.exists()) {
+ deleteDbFileWarnIfFailed(ceDb);
+ }
+ }
+ }
+
+ private static void deleteDbFileWarnIfFailed(File dbFile) {
+ if (!SQLiteDatabase.deleteDatabase(dbFile)) {
+ Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
}
}
private void onUserUnlocked(Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onUserUnlocked " + userId);
+ }
+ synchronized (mUsers) {
+ mUnlockedUsers.put(userId, true);
+ }
if (userId < 1) return;
+ syncSharedAccounts(userId);
+ }
+ private void syncSharedAccounts(int userId) {
// Check if there's a shared account that needs to be created as an account
Account[] sharedAccounts = getSharedAccountsAsUser(userId);
if (sharedAccounts == null || sharedAccounts.length == 0) return;
@@ -645,20 +697,15 @@ public class AccountManagerService
if (account == null) {
return null;
}
+ if (!isUserUnlocked(accounts.userId)) {
+ Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
+ return null;
+ }
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
- Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
- ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
- new String[]{account.name, account.type}, null, null, null);
- try {
- if (cursor.moveToNext()) {
- return cursor.getString(0);
- }
- return null;
- } finally {
- cursor.close();
- }
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
+ return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
+ account.type);
}
}
@@ -699,7 +746,7 @@ public class AccountManagerService
try {
if (cursor.moveToNext()) {
String previousName = cursor.getString(0);
- previousNameRef = new AtomicReference<String>(previousName);
+ previousNameRef = new AtomicReference<>(previousName);
accounts.previousNameCache.put(account, previousNameRef);
return previousName;
} else {
@@ -825,7 +872,7 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- return addAccountInternal(accounts, account, password, extras, false, callingUid);
+ return addAccountInternal(accounts, account, password, extras, callingUid);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -999,17 +1046,22 @@ public class AccountManagerService
}
private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
- Bundle extras, boolean restricted, int callingUid) {
+ Bundle extras, int callingUid) {
Bundle.setDefusable(extras, true);
if (account == null) {
return false;
}
+ if (!isUserUnlocked(accounts.userId)) {
+ Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
+ + " is locked. callingUid=" + callingUid);
+ return false;
+ }
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
long numMatches = DatabaseUtils.longForQuery(db,
- "select count(*) from " + TABLE_ACCOUNTS
+ "select count(*) from " + CE_TABLE_ACCOUNTS
+ " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
new String[]{account.name, account.type});
if (numMatches > 0) {
@@ -1021,13 +1073,24 @@ public class AccountManagerService
values.put(ACCOUNTS_NAME, account.name);
values.put(ACCOUNTS_TYPE, account.type);
values.put(ACCOUNTS_PASSWORD, password);
- values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
- long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+ long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account
+ ", skipping the DB insert failed");
return false;
}
+ // Insert into DE table
+ values = new ContentValues();
+ values.put(ACCOUNTS_ID, accountId);
+ values.put(ACCOUNTS_NAME, account.name);
+ values.put(ACCOUNTS_TYPE, account.type);
+ values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
+ System.currentTimeMillis());
+ if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
+ Log.w(TAG, "insertAccountIntoDatabase: " + account
+ + ", skipping the DB insert failed");
+ return false;
+ }
if (extras != null) {
for (String key : extras.keySet()) {
final String value = extras.getString(key);
@@ -1055,6 +1118,12 @@ public class AccountManagerService
return true;
}
+ private boolean isUserUnlocked(int userId) {
+ synchronized (mUsers) {
+ return mUnlockedUsers.get(userId);
+ }
+ }
+
/**
* Adds the account to all linked restricted users as shared accounts. If the user is currently
* running, then clone the account too.
@@ -1079,7 +1148,7 @@ public class AccountManagerService
values.put(EXTRAS_KEY, key);
values.put(EXTRAS_ACCOUNTS_ID, accountId);
values.put(EXTRAS_VALUE, value);
- return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
+ return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
}
@Override
@@ -1226,17 +1295,19 @@ public class AccountManagerService
}
}
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
boolean isSuccessful = false;
Account renamedAccount = new Account(newName, accountToRename.type);
try {
- final ContentValues values = new ContentValues();
- values.put(ACCOUNTS_NAME, newName);
- values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
final long accountId = getAccountIdLocked(db, accountToRename);
if (accountId >= 0) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
final String[] argsAccountId = { String.valueOf(accountId) };
+ db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ // Update NAME/PREVIOUS_NAME in DE accounts table
+ values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
db.setTransactionSuccessful();
isSuccessful = true;
@@ -1332,7 +1403,7 @@ public class AccountManagerService
* authenticator. This will let users remove accounts (via Settings in the system) but not
* arbitrary applications (like competing authenticators).
*/
- UserHandle user = new UserHandle(userId);
+ UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
&& !isSystemUid(callingUid)) {
String msg = String.format(
@@ -1467,13 +1538,17 @@ public class AccountManagerService
}
private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
+ // For now user is required to be unlocked. TODO: Handle both cases in the future
int deleted;
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
final long accountId = getAccountIdLocked(db, account);
deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+ "=?",
new String[]{account.name, account.type});
+ // Delete from CE table
+ db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{account.name, account.type});
removeAccountFromCacheLocked(accounts, account);
sendAccountsChangedBroadcast(accounts.userId);
@@ -1512,7 +1587,7 @@ public class AccountManagerService
try {
UserAccounts accounts = getUserAccounts(userId);
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
invalidateAuthTokenLocked(accounts, db, accountType, authToken);
@@ -1544,22 +1619,22 @@ public class AccountManagerService
return;
}
Cursor cursor = db.rawQuery(
- "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
- + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
- + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
- + " FROM " + TABLE_ACCOUNTS
- + " JOIN " + TABLE_AUTHTOKENS
- + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
- + " = " + AUTHTOKENS_ACCOUNTS_ID
- + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
- + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+ "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+ + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+ + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+ + " FROM " + CE_TABLE_ACCOUNTS
+ + " JOIN " + CE_TABLE_AUTHTOKENS
+ + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+ + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
+ + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
+ + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
new String[]{authToken, accountType});
try {
while (cursor.moveToNext()) {
long authTokenId = cursor.getLong(0);
String accountName = cursor.getString(1);
String authTokenType = cursor.getString(2);
- db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+ db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
writeAuthTokenIntoCacheLocked(
accounts,
db,
@@ -1585,7 +1660,7 @@ public class AccountManagerService
return;
}
cancelNotification(getSigninRequiredNotificationId(accounts, account),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
synchronized (accounts.cacheLock) {
accounts.accountTokenCaches.put(
account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
@@ -1598,23 +1673,23 @@ public class AccountManagerService
return false;
}
cancelNotification(getSigninRequiredNotificationId(accounts, account),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
if (accountId < 0) {
return false;
}
- db.delete(TABLE_AUTHTOKENS,
+ db.delete(CE_TABLE_AUTHTOKENS,
AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
new String[]{type});
ContentValues values = new ContentValues();
values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
values.put(AUTHTOKENS_TYPE, type);
values.put(AUTHTOKENS_AUTHTOKEN, authToken);
- if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
+ if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
db.setTransactionSuccessful();
writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
return true;
@@ -1714,7 +1789,7 @@ public class AccountManagerService
return;
}
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
final ContentValues values = new ContentValues();
@@ -1722,8 +1797,8 @@ public class AccountManagerService
final long accountId = getAccountIdLocked(db, account);
if (accountId >= 0) {
final String[] argsAccountId = {String.valueOf(accountId)};
- db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
- db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
+ db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
accounts.authTokenCache.remove(account);
accounts.accountTokenCaches.remove(account);
db.setTransactionSuccessful();
@@ -1806,7 +1881,7 @@ public class AccountManagerService
return;
}
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
@@ -1822,7 +1897,7 @@ public class AccountManagerService
} else {
ContentValues values = new ContentValues();
values.put(EXTRAS_VALUE, value);
- if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+ if (1 != db.update(CE_TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
return;
}
@@ -3307,7 +3382,6 @@ public class AccountManagerService
long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
final ContentValues values = new ContentValues();
values.put(ACCOUNTS_NAME, newName);
- values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
int r = db.update(
TABLE_SHARED_ACCOUNTS,
values,
@@ -3347,7 +3421,7 @@ public class AccountManagerService
public Account[] getSharedAccountsAsUser(int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
- ArrayList<Account> accountList = new ArrayList<Account>();
+ ArrayList<Account> accountList = new ArrayList<>();
Cursor cursor = null;
try {
cursor = accounts.openHelper.getReadableDatabase()
@@ -3488,7 +3562,7 @@ public class AccountManagerService
}
private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
- Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
+ Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
new String[]{key}, null, null, null);
try {
@@ -3729,8 +3803,8 @@ public class AccountManagerService
if (accountPresent) {
lastAuthenticatedTime = DatabaseUtils.longForQuery(
mAccounts.openHelper.getReadableDatabase(),
- "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
- + " from " +
+ "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " FROM " +
TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ ACCOUNTS_TYPE + "=?",
new String[] {
@@ -3859,7 +3933,7 @@ public class AccountManagerService
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
- new UserHandle(mAccounts.userId))) {
+ UserHandle.of(mAccounts.userId))) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
}
@@ -3893,15 +3967,16 @@ public class AccountManagerService
}
}
- private static String getDatabaseName(int userId) {
+ static String getPreNDatabaseName(int userId) {
File systemDir = Environment.getDataSystemDirectory();
- File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
+ File databaseFile = new File(Environment.getUserSystemDirectory(userId),
+ PRE_N_DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
// Make sure the new file doesn't already exist. A dummy file could have been
// accidentally created in the old location, causing the new one to become corrupted
// as well.
- File oldFile = new File(systemDir, DATABASE_NAME);
+ File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
if (oldFile.exists() && !databaseFile.exists()) {
// Check for use directory; create if it doesn't exist, else renameTo will fail
File userDir = Environment.getUserSystemDirectory(userId);
@@ -3918,6 +3993,18 @@ public class AccountManagerService
return databaseFile.getPath();
}
+ static String getDeDatabaseName(int userId) {
+ File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
+ DE_DATABASE_NAME);
+ return databaseFile.getPath();
+ }
+
+ static String getCeDatabaseName(int userId) {
+ File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
+ CE_DATABASE_NAME);
+ return databaseFile.getPath();
+ }
+
private static class DebugDbHelper{
private DebugDbHelper() {
}
@@ -4050,58 +4137,22 @@ public class AccountManagerService
return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
}
- static class DatabaseHelper extends SQLiteOpenHelper {
+ static class PreNDatabaseHelper extends SQLiteOpenHelper {
private final Context mContext;
private final int mUserId;
- public DatabaseHelper(Context context, int userId) {
- super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
+ public PreNDatabaseHelper(Context context, int userId) {
+ super(context, AccountManagerService.getPreNDatabaseName(userId), null,
+ PRE_N_DATABASE_VERSION);
mContext = context;
mUserId = userId;
}
- /**
- * This call needs to be made while the mCacheLock is held. The way to
- * ensure this is to get the lock any time a method is called ont the DatabaseHelper
- * @param db The database.
- */
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
- + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ACCOUNTS_NAME + " TEXT NOT NULL, "
- + ACCOUNTS_TYPE + " TEXT NOT NULL, "
- + ACCOUNTS_PASSWORD + " TEXT, "
- + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
- + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
- + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
-
- db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
- + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
- + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
- + AUTHTOKENS_AUTHTOKEN + " TEXT, "
- + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
-
- createGrantsTable(db);
-
- db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
- + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + EXTRAS_ACCOUNTS_ID + " INTEGER, "
- + EXTRAS_KEY + " TEXT NOT NULL, "
- + EXTRAS_VALUE + " TEXT, "
- + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
-
- db.execSQL("CREATE TABLE " + TABLE_META + " ( "
- + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
- + META_VALUE + " TEXT)");
-
- createSharedAccountsTable(db);
-
- createAccountsDeletionTrigger(db);
-
- DebugDbHelper.createDebugTable(db);
+ // We use PreNDatabaseHelper only if pre-N db exists
+ throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
}
private void createSharedAccountsTable(SQLiteDatabase db) {
@@ -4161,6 +4212,9 @@ public class AccountManagerService
}
}
+ /**
+ * Pre-N database may need an upgrade before splitting
+ */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
@@ -4222,6 +4276,315 @@ public class AccountManagerService
}
}
+ static class DeDatabaseHelper extends SQLiteOpenHelper {
+
+ private final int mUserId;
+ private volatile boolean mCeAttached;
+
+ private DeDatabaseHelper(Context context, int userId) {
+ super(context, getDeDatabaseName(userId), null, DE_DATABASE_VERSION);
+ mUserId = userId;
+ }
+
+ /**
+ * This call needs to be made while the mCacheLock is held. The way to
+ * ensure this is to get the lock any time a method is called ont the DatabaseHelper
+ * @param db The database.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.i(TAG, "Creating DE database for user " + mUserId);
+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+ + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+ + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+ + META_VALUE + " TEXT)");
+
+ createGrantsTable(db);
+ createSharedAccountsTable(db);
+ createAccountsDeletionTrigger(db);
+ DebugDbHelper.createDebugTable(db);
+ }
+
+ private void createSharedAccountsTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+ }
+
+ private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_GRANTS
+ + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ }
+
+ private void createGrantsTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
+ + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
+ + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
+ + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
+ + "," + GRANTS_GRANTEE_UID + "))");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+ if (oldVersion != newVersion) {
+ Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+ }
+ }
+
+ public void attachCeDatabase() {
+ File ceDbFile = new File(getCeDatabaseName(mUserId));
+ SQLiteDatabase db = getWritableDatabase();
+ db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
+ mCeAttached = true;
+ }
+
+ public boolean isCeDatabaseAttached() {
+ return mCeAttached;
+ }
+
+
+ public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
+ if(!mCeAttached) {
+ Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user "
+ + mUserId + " is still locked ", new Throwable());
+ }
+ return super.getReadableDatabase();
+ }
+
+ public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
+ if(!mCeAttached) {
+ Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
+ + " is still locked ", new Throwable());
+ }
+ return super.getWritableDatabase();
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
+ }
+
+ private void migratePreNDbToDe(File preNDbFile) {
+ Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
+ SQLiteDatabase db = getWritableDatabase();
+ db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
+ db.beginTransaction();
+ // Copy accounts fields
+ db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
+ + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+ + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + ") "
+ + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+ + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " FROM preNDb." + TABLE_ACCOUNTS);
+ // Copy SHARED_ACCOUNTS
+ db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
+ + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
+ "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
+ + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
+ // Copy DEBUG_TABLE
+ db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
+ + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+ + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+ + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
+ "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+ + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+ + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
+ + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
+ // Copy GRANTS
+ db.execSQL("INSERT INTO " + TABLE_GRANTS
+ + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+ + GRANTS_GRANTEE_UID + ") " +
+ "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+ + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
+ // Copy META
+ db.execSQL("INSERT INTO " + TABLE_META
+ + "(" + META_KEY + "," + META_VALUE + ") "
+ + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+
+ db.execSQL("DETACH DATABASE preNDb");
+ }
+
+ static DeDatabaseHelper create(Context context, int userId) {
+ File oldDb = new File(getPreNDatabaseName(userId));
+ File newDb = new File(getDeDatabaseName(userId));
+ boolean newDbExists = newDb.exists();
+ DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId);
+ // If the db just created, and there is a legacy db, migrate it
+ if (!newDbExists && oldDb.exists()) {
+ // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
+ PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId);
+ // Open the database to force upgrade if required
+ preNDatabaseHelper.getWritableDatabase();
+ preNDatabaseHelper.close();
+ // Move data without SPII to DE
+ deDatabaseHelper.migratePreNDbToDe(oldDb);
+ }
+ return deDatabaseHelper;
+ }
+ }
+
+ static class CeDatabaseHelper extends SQLiteOpenHelper {
+
+ public CeDatabaseHelper(Context context, int userId) {
+ super(context, getCeDatabaseName(userId), null, CE_DATABASE_VERSION);
+ }
+
+ /**
+ * This call needs to be made while the mCacheLock is held.
+ * @param db The database.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.i(TAG, "Creating CE database " + getDatabaseName());
+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + ACCOUNTS_PASSWORD + " TEXT, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
+ + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
+ + AUTHTOKENS_AUTHTOKEN + " TEXT, "
+ + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
+ + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + EXTRAS_ACCOUNTS_ID + " INTEGER, "
+ + EXTRAS_KEY + " TEXT NOT NULL, "
+ + EXTRAS_VALUE + " TEXT, "
+ + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
+
+ createAccountsDeletionTrigger(db);
+ }
+
+ private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_AUTHTOKENS
+ + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_EXTRAS
+ + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
+
+ if (oldVersion == 9) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onUpgrade upgrading to v10");
+ }
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
+ // Recreate the trigger, since the old one references the table to be removed
+ db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
+ createAccountsDeletionTrigger(db);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
+ db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
+ oldVersion ++;
+ }
+
+ if (oldVersion != newVersion) {
+ Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+ }
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
+ }
+
+ static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
+ String type) {
+ Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{name, type}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ }
+ return null;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
+ * it also performs migration to the new CE database.
+ * @param context
+ * @param userId id of the user where the database is located
+ */
+ static CeDatabaseHelper create(Context context, int userId) {
+
+ File oldDatabaseFile = new File(getPreNDatabaseName(userId));
+ File ceDatabaseFile = new File(getCeDatabaseName(userId));
+ boolean newDbExists = ceDatabaseFile.exists();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
+ + oldDatabaseFile.exists() + " newDbExists=" + newDbExists);
+ }
+ boolean removeOldDb = false;
+ if (!newDbExists && oldDatabaseFile.exists()) {
+ removeOldDb = migratePreNDbToCe(oldDatabaseFile, ceDatabaseFile);
+ }
+ // Try to open and upgrade if necessary
+ CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, userId);
+ ceHelper.getWritableDatabase();
+ ceHelper.close();
+ if (removeOldDb) {
+ // TODO STOPSHIP - backup file during testing. Remove file before the release
+ Log.i(TAG, "Migration complete - creating backup of old db " + oldDatabaseFile);
+ renameToBakFile(oldDatabaseFile);
+ }
+ return ceHelper;
+ }
+
+ private static void renameToBakFile(File file) {
+ File bakFile = new File(file.getPath() + ".bak");
+ if (!file.renameTo(bakFile)) {
+ Log.e(TAG, "Cannot move file to " + bakFile);
+ }
+ }
+
+ private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
+ Log.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
+ try {
+ FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
+ // Try to remove potentially damaged file if I/O error occurred
+ deleteDbFileWarnIfFailed(ceDbFile);
+ return false;
+ }
+ return true;
+ }
+ }
+
public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
return asBinder();
}
@@ -4666,7 +5029,7 @@ public class AccountManagerService
db.endTransaction();
}
cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
}
}
@@ -4891,7 +5254,7 @@ public class AccountManagerService
HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
if (authTokensForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
accounts.authTokenCache.put(account, authTokensForAccount);
}
@@ -4904,7 +5267,7 @@ public class AccountManagerService
HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
if (userDataForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
accounts.userDataCache.put(account, userDataForAccount);
}
@@ -4914,8 +5277,8 @@ public class AccountManagerService
protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
final SQLiteDatabase db, Account account) {
- HashMap<String, String> userDataForAccount = new HashMap<String, String>();
- Cursor cursor = db.query(TABLE_EXTRAS,
+ HashMap<String, String> userDataForAccount = new HashMap<>();
+ Cursor cursor = db.query(CE_TABLE_EXTRAS,
COLUMNS_EXTRAS_KEY_AND_VALUE,
SELECTION_USERDATA_BY_ACCOUNT,
new String[]{account.name, account.type},
@@ -4934,8 +5297,8 @@ public class AccountManagerService
protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
final SQLiteDatabase db, Account account) {
- HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
- Cursor cursor = db.query(TABLE_AUTHTOKENS,
+ HashMap<String, String> authTokensForAccount = new HashMap<>();
+ Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
SELECTION_AUTHTOKENS_BY_ACCOUNT,
new String[]{account.name, account.type},
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f99caba59b40..d741c4948280 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -940,7 +940,7 @@ public final class ActiveServices {
}
}
- mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
+ mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 42cf42fdd650..e82c6c7a22a4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -891,6 +891,12 @@ public final class ActivityManagerService extends ActivityManagerNative
int mNesting;
long mStartTime;
+ // states of the source process when the bind occurred.
+ int mLastState = ActivityManager.MAX_PROCESS_STATE + 1;
+ long mLastStateUptime;
+ long[] mStateTimes = new long[ActivityManager.MAX_PROCESS_STATE
+ - ActivityManager.MIN_PROCESS_STATE+1];
+
Association(int sourceUid, String sourceProcess, int targetUid,
ComponentName targetComponent, String targetProcess) {
mSourceUid = sourceUid;
@@ -6056,8 +6062,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"No more processes in " + old.uidRecord);
enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
mActiveUids.remove(uid);
- mBatteryStatsService.noteUidProcessState(uid,
- ActivityManager.PROCESS_STATE_NONEXISTENT);
+ noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
old.uidRecord = null;
}
@@ -6082,7 +6087,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
mActiveUids.put(proc.uid, uidRec);
- mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ noteUidProcessState(uidRec.uid, uidRec.curProcState);
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
@@ -10190,7 +10195,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
cpr.connections.add(conn);
r.conProviders.add(conn);
- startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName);
+ startAssociationLocked(r.uid, r.processName, r.curProcState,
+ cpr.uid, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken);
@@ -13797,10 +13803,27 @@ public final class ActivityManagerService extends ActivityManagerNative
TimeUtils.formatDuration(dur, pw);
pw.print(" (");
pw.print(ass.mCount);
- pw.println(" times)");
+ pw.print(" times)");
+ pw.print(" ");
+ for (int i=0; i<ass.mStateTimes.length; i++) {
+ long amt = ass.mStateTimes[i];
+ if (ass.mLastState-ActivityManager.MIN_PROCESS_STATE == i) {
+ amt += now - ass.mLastStateUptime;
+ }
+ if (amt != 0) {
+ pw.print(" ");
+ pw.print(ProcessList.makeProcStateString(
+ i + ActivityManager.MIN_PROCESS_STATE));
+ pw.print("=");
+ TimeUtils.formatDuration(amt, pw);
+ if (ass.mLastState-ActivityManager.MIN_PROCESS_STATE == i) {
+ pw.print("*");
+ }
+ }
+ }
+ pw.println();
if (ass.mNesting > 0) {
- pw.print(" ");
- pw.print(" Currently active: ");
+ pw.print(" Currently active: ");
TimeUtils.formatDuration(now - ass.mStartTime, pw);
pw.println();
}
@@ -18238,8 +18261,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
- Association startAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
- ComponentName targetComponent, String targetProcess) {
+ Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
+ int targetUid, ComponentName targetComponent, String targetProcess) {
if (!mTrackingAssociations) {
return null;
}
@@ -18268,7 +18291,8 @@ public final class ActivityManagerService extends ActivityManagerNative
ass.mCount++;
ass.mNesting++;
if (ass.mNesting == 1) {
- ass.mStartTime = SystemClock.uptimeMillis();
+ ass.mStartTime = ass.mLastStateUptime = SystemClock.uptimeMillis();
+ ass.mLastState = sourceState;
}
return ass;
}
@@ -18297,7 +18321,39 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ass.mNesting--;
if (ass.mNesting == 0) {
- ass.mTime += SystemClock.uptimeMillis() - ass.mStartTime;
+ long uptime = SystemClock.uptimeMillis();
+ ass.mTime += uptime - ass.mStartTime;
+ ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE]
+ += uptime - ass.mLastStateUptime;
+ ass.mLastState = ActivityManager.MAX_PROCESS_STATE + 2;
+ }
+ }
+
+ private void noteUidProcessState(final int uid, final int state) {
+ mBatteryStatsService.noteUidProcessState(uid, state);
+ if (mTrackingAssociations) {
+ for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) {
+ ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents
+ = mAssociations.valueAt(i1);
+ for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) {
+ SparseArray<ArrayMap<String, Association>> sourceUids
+ = targetComponents.valueAt(i2);
+ ArrayMap<String, Association> sourceProcesses = sourceUids.get(uid);
+ if (sourceProcesses != null) {
+ for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) {
+ Association ass = sourceProcesses.valueAt(i4);
+ if (ass.mNesting >= 1) {
+ // currently associated
+ long uptime = SystemClock.uptimeMillis();
+ ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE]
+ += uptime - ass.mLastStateUptime;
+ ass.mLastState = state;
+ ass.mLastStateUptime = uptime;
+ }
+ }
+ }
+ }
+ }
}
}
@@ -20176,7 +20232,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
uidRec.setProcState = uidRec.curProcState;
enqueueUidChangeLocked(uidRec, -1, uidChange);
- mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ noteUidProcessState(uidRec.uid, uidRec.curProcState);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4ca615dd55c9..d13f47266f25 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -42,7 +42,7 @@ import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
/**
* Helper class for running dexopt command on packages.
@@ -144,9 +144,10 @@ class PackageDexOptimizer {
if (isUsedByOtherApps(path)) {
checkProfiles = false;
- // TODO: Should we only upgrade to the non-profile-guided version? That is,
- // given verify-profile, should we move to interpret-only?
- targetCompilerFilter = getFullCompilerFilter();
+ targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
+ throw new IllegalStateException(targetCompilerFilter);
+ }
isProfileGuidedFilter = false;
break;
@@ -181,6 +182,10 @@ class PackageDexOptimizer {
return DEX_OPT_FAILED;
}
dexoptNeeded = adjustDexoptNeeded(dexoptNeeded);
+ if (PackageManagerService.DEBUG_DEXOPT) {
+ Log.i(TAG, "DexoptNeeded for " + path + "@" + targetCompilerFilter + " is " +
+ dexoptNeeded);
+ }
final String dexoptType;
String oatDir = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index f1b7991819b7..a7512db55615 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -126,4 +126,10 @@ class PackageManagerServiceCompilerMapping {
return value;
}
+ /**
+ * Return the non-profile-guided filter corresponding to the given filter.
+ */
+ public static String getNonProfileGuidedCompilerFilter(String filter) {
+ return DexFile.getNonProfileGuidedCompilerFilter(filter);
+ }
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 50699f8ccf4a..c4f9cc125411 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -26,9 +26,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.os.Binder;
+import android.os.Build;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
@@ -38,6 +40,7 @@ import android.os.UserManager;
import android.provider.Settings.Global;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
+import android.util.Base64;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
import android.webkit.WebViewFactory;
@@ -72,8 +75,6 @@ public class WebViewUpdateService extends SystemService {
// The WebView package currently in use (or the one we are preparing).
private PackageInfo mCurrentWebViewPackage = null;
- // The WebView providers that are currently available.
- private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
private BroadcastReceiver mWebViewUpdatedReceiver;
private WebViewUtilityInterface mWebViewUtility;
@@ -126,7 +127,6 @@ public class WebViewUpdateService extends SystemService {
PackageInfo newPackage = null;
synchronized(WebViewUpdateService.this) {
try {
- updateValidWebViewPackages();
newPackage = findPreferredWebViewPackage();
if (mCurrentWebViewPackage != null)
oldProviderName = mCurrentWebViewPackage.packageName;
@@ -180,11 +180,17 @@ public class WebViewUpdateService extends SystemService {
publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
}
- private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
+ private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
for (WebViewProviderInfo provider : providers) {
- if (provider.isAvailableByDefault() && provider.isEnabled()
- && provider.isValidProvider() && !provider.isFallbackPackage()) {
- return true;
+ if (provider.availableByDefault && !provider.isFallback) {
+ try {
+ PackageInfo packageInfo = getPackageInfoForProvider(provider);
+ if (isEnabledPackage(packageInfo) && isValidProvider(provider, packageInfo)) {
+ return true;
+ }
+ } catch (NameNotFoundException e) {
+ // A non-existent provider is neither valid nor enabled
+ }
}
}
return false;
@@ -211,11 +217,9 @@ public class WebViewUpdateService extends SystemService {
WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
- boolean existsValidNonFallbackProvider =
- existsValidNonFallbackProvider(webviewProviders);
- enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
- userId);
+ enablePackageForUser(fallbackProvider.packageName,
+ !existsValidNonFallbackProvider(webviewProviders), userId);
}
/**
@@ -236,7 +240,7 @@ public class WebViewUpdateService extends SystemService {
for (WebViewProviderInfo provider : webviewProviders) {
String webviewPackage = "package:" + provider.packageName;
if (webviewPackage.equals(intent.getDataString())) {
- if (provider.isAvailableByDefault()) {
+ if (provider.availableByDefault) {
changedPackage = provider.packageName;
}
break;
@@ -251,10 +255,16 @@ public class WebViewUpdateService extends SystemService {
if (fallbackProvider == null) return;
boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
+ boolean isFallbackEnabled = false;
+ try {
+ isFallbackEnabled = isEnabledPackage(getPackageInfoForProvider(fallbackProvider));
+ } catch (NameNotFoundException e) {
+ }
+
if (existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
- && (fallbackProvider.isEnabled() || intent == null)) {
+ && (isFallbackEnabled || intent == null)) {
// Uninstall and disable fallback package for all users.
context.getPackageManager().deletePackage(fallbackProvider.packageName,
new IPackageDeleteObserver.Stub() {
@@ -273,7 +283,7 @@ public class WebViewUpdateService extends SystemService {
} else if (!existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
- && (!fallbackProvider.isEnabled() || intent==null)) {
+ && (!isFallbackEnabled || intent==null)) {
// Enable the fallback package for all users.
UserManager userManager =
(UserManager)context.getSystemService(Context.USER_SERVICE);
@@ -299,24 +309,13 @@ public class WebViewUpdateService extends SystemService {
*/
private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
for (WebViewProviderInfo provider : webviewPackages) {
- if (provider.isFallbackPackage()) {
+ if (provider.isFallback) {
return provider;
}
}
return null;
}
- private static boolean containsAvailableNonFallbackProvider(
- WebViewProviderInfo[] webviewPackages) {
- for (WebViewProviderInfo provider : webviewPackages) {
- if (provider.isAvailableByDefault() && provider.isEnabled()
- && provider.isValidProvider() && !provider.isFallbackPackage()) {
- return true;
- }
- }
- return false;
- }
-
private boolean isFallbackPackage(String packageName) {
if (packageName == null || !isFallbackLogicEnabled()) return false;
@@ -336,7 +335,6 @@ public class WebViewUpdateService extends SystemService {
updateFallbackState(getContext(), null);
try {
synchronized(this) {
- updateValidWebViewPackages();
mCurrentWebViewPackage = findPreferredWebViewPackage();
onWebViewProviderChanged(mCurrentWebViewPackage);
}
@@ -408,24 +406,41 @@ public class WebViewUpdateService extends SystemService {
}
}
+ private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+ WebViewProviderInfo[] allProviders = mWebViewUtility.getWebViewPackages();
+ List<ProviderAndPackageInfo> providers = new ArrayList<>();
+ for(int n = 0; n < allProviders.length; n++) {
+ try {
+ PackageInfo packageInfo = getPackageInfoForProvider(allProviders[n]);
+ if (isValidProvider(allProviders[n], packageInfo)) {
+ providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+ }
+ } catch (NameNotFoundException e) {
+ // Don't add non-existent packages
+ }
+ }
+ return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+ }
+
/**
- * Updates the currently valid WebView provider packages.
- * Should be used when a provider has been installed or removed.
- * @hide
- * */
- private void updateValidWebViewPackages() {
- List<WebViewProviderInfo> webViewProviders =
- new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
- Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
- // remove non-valid packages
- while(it.hasNext()) {
- WebViewProviderInfo current = it.next();
- if (!current.isValidProvider())
- it.remove();
+ * Fetch only the currently valid WebView packages.
+ **/
+ private WebViewProviderInfo[] getValidWebViewPackages() {
+ ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+ WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length];
+ for(int n = 0; n < providersAndPackageInfos.length; n++) {
+ providers[n] = providersAndPackageInfos[n].provider;
}
- synchronized(this) {
- mCurrentValidWebViewPackages =
- webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
+ return providers;
+ }
+
+ private class ProviderAndPackageInfo {
+ public final WebViewProviderInfo provider;
+ public final PackageInfo packageInfo;
+
+ public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+ this.provider = provider;
+ this.packageInfo = packageInfo;
}
}
@@ -437,28 +452,30 @@ public class WebViewUpdateService extends SystemService {
* @hide
*/
private PackageInfo findPreferredWebViewPackage() {
- WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
+ ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());
// If the user has chosen provider, use that
- for (WebViewProviderInfo provider : providers) {
- if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
- return provider.getPackageInfo();
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.packageName.equals(userChosenProvider)
+ && isEnabledPackage(providerAndPackage.packageInfo)) {
+ return providerAndPackage.packageInfo;
}
}
// User did not choose, or the choice failed; use the most stable provider that is
// enabled and available by default (not through user choice).
- for (WebViewProviderInfo provider : providers) {
- if (provider.isAvailableByDefault() && provider.isEnabled()) {
- return provider.getPackageInfo();
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.availableByDefault
+ && isEnabledPackage(providerAndPackage.packageInfo)) {
+ return providerAndPackage.packageInfo;
}
}
// Could not find any enabled package either, use the most stable provider.
- for (WebViewProviderInfo provider : providers) {
- return provider.getPackageInfo();
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ return providerAndPackage.packageInfo;
}
mAnyWebViewInstalled = false;
@@ -467,6 +484,60 @@ public class WebViewUpdateService extends SystemService {
}
/**
+ * Returns whether this provider is valid for use as a WebView provider.
+ */
+ private static boolean isValidProvider(WebViewProviderInfo configInfo,
+ PackageInfo packageInfo) {
+ if (providerHasValidSignature(configInfo, packageInfo) &&
+ WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+ PackageInfo packageInfo) {
+ if (Build.IS_DEBUGGABLE)
+ return true;
+ Signature[] packageSignatures;
+ // If no signature is declared, instead check whether the package is included in the
+ // system.
+ if (provider.signatures == null || provider.signatures.length == 0) {
+ return packageInfo.applicationInfo.isSystemApp();
+ }
+ packageSignatures = packageInfo.signatures;
+ if (packageSignatures.length != 1)
+ return false;
+
+ final byte[] packageSignature = packageSignatures[0].toByteArray();
+ // Return whether the package signature matches any of the valid signatures
+ for (String signature : provider.signatures) {
+ final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+ if (Arrays.equals(packageSignature, validSignature))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the given package is enabled.
+ * This state can be changed by the user from Settings->Apps
+ */
+ private static boolean isEnabledPackage(PackageInfo packageInfo) {
+ return packageInfo.applicationInfo.enabled;
+ }
+
+ private static PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
+ throws NameNotFoundException {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
+ }
+
+ // flags declaring we want extra info from the package manager for webview providers
+ private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
+ | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+
+ /**
* Returns whether WebView is ready and is not going to go through its preparation phase again
* directly.
*/
@@ -593,9 +664,7 @@ public class WebViewUpdateService extends SystemService {
@Override // Binder call
public WebViewProviderInfo[] getValidWebViewPackages() {
- synchronized(WebViewUpdateService.this) {
- return mCurrentValidWebViewPackages;
- }
+ return WebViewUpdateService.this.getValidWebViewPackages();
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
index 4dbd02d1ede5..aaa7977208a1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
@@ -87,10 +87,10 @@ public class WebViewUtilityImpl implements WebViewUtilityInterface {
parser.getAttributeValue(null, TAG_AVAILABILITY));
boolean isFallback = "true".equals(
parser.getAttributeValue(null, TAG_FALLBACK));
- WebViewProviderInfo currentProvider =
- new WebViewProviderInfo(packageName, description, availableByDefault,
- isFallback, readSignatures(parser));
- if (currentProvider.isFallbackPackage()) {
+ WebViewProviderInfo currentProvider = new WebViewProviderInfo(
+ packageName, description, availableByDefault, isFallback,
+ readSignatures(parser));
+ if (currentProvider.isFallback) {
numFallbackPackages++;
if (numFallbackPackages > 1) {
throw new AndroidRuntimeException(
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index ec5e8c921979..14d50ce957fe 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -36,7 +36,8 @@ namespace android {
enum {
TEMPERATURE_CURRENT = 0,
TEMPERATURE_THROTTLING = 1,
- TEMPERATURE_SHUTDOWN = 2
+ TEMPERATURE_SHUTDOWN = 2,
+ TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3
};
static struct {
@@ -127,6 +128,13 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */,
values[length++] = list[i].shutdown_threshold;
}
break;
+ case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
+ if (list[i].vr_throttling_threshold == UNKNOWN_TEMPERATURE) {
+ values[length++] = gUndefinedTemperature;
+ } else {
+ values[length++] = list[i].vr_throttling_threshold;
+ }
+ break;
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 22480738c587..ac6fc5855dfb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1480,6 +1480,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return "/data/system/";
}
+ void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer, int userHandle) {
+ mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents,
+ observer, userHandle);
+ }
+
int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
name, def, userHandle);
@@ -2536,7 +2542,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
onStartUser(UserHandle.USER_SYSTEM);
// Register an observer for watching for user setup complete.
- new SetupContentObserver(mHandler).register(mContext.getContentResolver());
+ new SetupContentObserver(mHandler).register();
// Initialize the user setup state, to handle the upgrade case.
updateUserSetupComplete();
@@ -7847,9 +7853,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
super(handler);
}
- void register(ContentResolver resolver) {
- resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
+ void register() {
+ mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+ mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
}
@Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b026bc577e2d..0f5930244f6e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -155,6 +155,8 @@ public final class SystemServer {
"com.android.server.search.SearchManagerService$Lifecycle";
private static final String THERMAL_OBSERVER_CLASS =
"com.google.android.clockwork.ThermalObserver";
+ private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
+ "com.google.android.clockwork.bluetooth.WearBluetoothService";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -959,8 +961,7 @@ public final class SystemServer {
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
- if (context.getPackageManager().hasSystemFeature
- (PackageManager.FEATURE_WATCH)) {
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
}
}
@@ -1172,6 +1173,10 @@ public final class SystemServer {
mSystemServiceManager.startService(MediaProjectionManagerService.class);
}
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(WEAR_BLUETOOTH_SERVICE_CLASS);
+ }
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 35777ce86c4a..744443f1d7e7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,14 +15,14 @@
*/
package com.android.server.devicepolicy;
-import com.android.internal.widget.LockPatternUtils;
-
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.database.ContentObserver;
import android.media.IAudioService;
+import android.net.Uri;
import android.os.Looper;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
@@ -30,12 +30,15 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
import android.view.IWindowManager;
-import java.io.File;
+import com.android.internal.widget.LockPatternUtils;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.when;
+import java.io.File;
+import java.util.Map;
/**
* Overrides {@link #DevicePolicyManagerService} for dependency injection.
@@ -77,6 +80,7 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
public final DpmMockContext context;
+ private final MockInjector mMockInjector;
public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) {
this(new MockInjector(context, dataDir));
@@ -84,15 +88,36 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
private DevicePolicyManagerServiceTestable(MockInjector injector) {
super(injector);
+ mMockInjector = injector;
this.context = injector.context;
}
+
+ public void notifyChangeToContentObserver(Uri uri, int userHandle) {
+ ContentObserver co = mMockInjector.mContentObservers
+ .get(new Pair<Uri, Integer>(uri, userHandle));
+ if (co != null) {
+ co.onChange(false, uri, userHandle); // notify synchronously
+ }
+
+ // Notify USER_ALL observer too.
+ co = mMockInjector.mContentObservers
+ .get(new Pair<Uri, Integer>(uri, UserHandle.USER_ALL));
+ if (co != null) {
+ co.onChange(false, uri, userHandle); // notify synchronously
+ }
+ }
+
+
private static class MockInjector extends Injector {
public final DpmMockContext context;
public final File dataDir;
+ // Key is a pair of uri and userId
+ private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>();
+
private MockInjector(DpmMockContext context, File dataDir) {
super(context);
this.context = context;
@@ -265,6 +290,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
}
@Override
+ void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer, int userHandle) {
+ mContentObservers.put(new Pair<Uri, Integer>(uri, userHandle), observer);
+ }
+
+ @Override
int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return context.settings.settingsSecureGetIntForUser(name, def, userHandle);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e6963d52e88a..3a2e946e617f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -543,10 +543,31 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
/**
- * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs
- * successfully.
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
*/
public void testSetDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ // Try to set a profile owner on the same user, which should fail.
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
+ try {
+ dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
+ fail("IllegalStateException not thrown");
+ } catch (IllegalStateException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("already has a device owner"));
+ }
+
+ // DO admin can't be deactivated.
+ dpm.removeActiveAdmin(admin1);
+ assertTrue(dpm.isAdminActive(admin1));
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ }
+
+ private void setDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -594,24 +615,6 @@ public class DevicePolicyManagerTest extends DpmTestBase {
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
-
- // Try to set a profile owner on the same user, which should fail.
- setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
- dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
- try {
- dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
- fail("IllegalStateException not thrown");
- } catch (IllegalStateException expected) {
- assertTrue("Message was: " + expected.getMessage(),
- expected.getMessage().contains("already has a device owner"));
- }
-
- // DO admin can't be deactivated.
- dpm.removeActiveAdmin(admin1);
- assertTrue(dpm.isAdminActive(admin1));
-
- // TODO Test getDeviceOwnerName() too. To do so, we need to change
- // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) {
@@ -1934,5 +1937,211 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// TODO Verify calls to settingsGlobalPutInt. Tried but somehow mockito threw
// UnfinishedVerificationException.
}
-}
+ public void testIsProvisioningAllowed_DeviceAdminFeatureOff() throws Exception {
+ when(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN))
+ .thenReturn(false);
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(false);
+ initializeDpms();
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+ }
+
+ public void testIsProvisioningAllowed_ManagedProfileFeatureOff() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(false);
+ initializeDpms();
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+
+ // Test again when split user is on
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+ }
+
+ public void testIsProvisioningAllowed_nonSplitUser_firstBoot_primaryUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false /* because of non-split user */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false /* because of non-split user */);
+ }
+
+ public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser()
+ throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ false/* because of completed device setup */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false/* because of non-split user */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because of non-split user */);
+ }
+
+ public void testIsProvisioningAllowed_splitUser_firstBoot_systemUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* because canAddMoreManagedProfiles returns false */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because calling uid is system user */);
+
+ }
+
+ public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_systemUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* because canAddMoreManagedProfiles returns false */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because calling uid is system user */);
+ }
+
+ public void testIsProvisioningAllowed_splitUser_firstBoot_primaryUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, true);
+
+ }
+
+ public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_primaryUser()
+ throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(true, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because user setup completed */);
+ }
+
+ public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_systemUser()
+ throws Exception {
+ setDeviceOwner();
+
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* can't provision managed profile on system user */);
+ }
+
+ public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
+ throws Exception {
+ setDeviceOwner();
+
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ }
+
+ private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
+ when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
+ userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
+ dpms.notifyChangeToContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), userhandle);
+ }
+
+ private void assertProvisioningAllowed(String action, boolean expected) {
+ assertEquals("isProvisioningAllowed(" + action + ") returning unexpected result", expected,
+ dpm.isProvisioningAllowed(action));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 8e2ef703f3c6..60d7382a7233 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -53,6 +53,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -172,36 +173,36 @@ public class DpmMockContext extends MockContext {
}
public static class SettingsForMock {
- int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+ public int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return 0;
}
- void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+ public void settingsSecurePutIntForUser(String name, int value, int userHandle) {
}
- void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+ public void settingsSecurePutStringForUser(String name, String value, int userHandle) {
}
- void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+ public void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
}
- void settingsSecurePutInt(String name, int value) {
+ public void settingsSecurePutInt(String name, int value) {
}
- void settingsGlobalPutInt(String name, int value) {
+ public void settingsGlobalPutInt(String name, int value) {
}
- void settingsSecurePutString(String name, String value) {
+ public void settingsSecurePutString(String name, String value) {
}
- void settingsGlobalPutString(String name, String value) {
+ public void settingsGlobalPutString(String name, String value) {
}
- int settingsGlobalGetInt(String name, int def) {
+ public int settingsGlobalGetInt(String name, int value) {
return 0;
}
- void securityLogSetLoggingEnabledProperty(boolean enabled) {
+ public void securityLogSetLoggingEnabledProperty(boolean enabled) {
}
public boolean securityLogGetLoggingEnabledProperty() {
@@ -321,6 +322,8 @@ public class DpmMockContext extends MockContext {
mUserInfos.add(uh);
when(userManager.getUsers()).thenReturn(mUserInfos);
+ when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos);
+ when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true);
when(userManager.getUserInfo(anyInt())).thenAnswer(
new Answer<UserInfo>() {
@Override