Merge "Fix animation start jank due to expensive layout operations."
diff --git a/api/current.txt b/api/current.txt
index 7e24003..26e977b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1696,6 +1696,8 @@
 
   public static final class R.id {
     ctor public R.id();
+    field public static final int accessibilityActionScrollToPosition = 16908342; // 0x1020036
+    field public static final int accessibilityActionShowOnScreen = 16908341; // 0x1020035
     field public static final int addToDictionary = 16908330; // 0x102002a
     field public static final int background = 16908288; // 0x1020000
     field public static final int button1 = 16908313; // 0x1020019
@@ -8015,6 +8017,7 @@
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
     field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
     field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
+    field public static final java.lang.String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
     field public static final java.lang.String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
     field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
     field public static final java.lang.String ACTION_REBOOT = "android.intent.action.REBOOT";
@@ -8115,6 +8118,8 @@
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+    field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+    field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
     field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
     field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
     field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
@@ -25837,6 +25842,7 @@
     method public static java.lang.String getVersion(android.content.Context);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+    field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
     field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
     field public static final java.lang.String AUTHORITY = "media";
     field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
@@ -36398,9 +36404,11 @@
     method public void setVisibleToUser(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
     field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
     field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
     field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -36458,6 +36466,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
diff --git a/api/system-current.txt b/api/system-current.txt
index 3f2e19b..2c3199d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1773,6 +1773,8 @@
 
   public static final class R.id {
     ctor public R.id();
+    field public static final int accessibilityActionScrollToPosition = 16908342; // 0x1020036
+    field public static final int accessibilityActionShowOnScreen = 16908341; // 0x1020035
     field public static final int addToDictionary = 16908330; // 0x102002a
     field public static final int background = 16908288; // 0x1020000
     field public static final int button1 = 16908313; // 0x1020019
@@ -8235,6 +8237,7 @@
     field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
     field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
     field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
+    field public static final java.lang.String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
     field public static final java.lang.String ACTION_PROVIDER_CHANGED = "android.intent.action.PROVIDER_CHANGED";
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_QUICK_CLOCK = "android.intent.action.QUICK_CLOCK";
@@ -8340,6 +8343,8 @@
     field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+    field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+    field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
     field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
     field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
     field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
@@ -27720,6 +27725,7 @@
     method public static java.lang.String getVersion(android.content.Context);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
+    field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
     field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
     field public static final java.lang.String AUTHORITY = "media";
     field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
@@ -38950,9 +38956,11 @@
     method public void setVisibleToUser(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
     field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
     field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
     field public static final java.lang.String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final java.lang.String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
     field public static final java.lang.String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -39010,6 +39018,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
@@ -40862,6 +40871,7 @@
     method public abstract boolean onKeyUp(int, android.view.KeyEvent);
     method public abstract void onMeasure(int, int);
     method public abstract void onOverScrolled(int, int, boolean, boolean);
+    method public abstract void onProvideVirtualAssistStructure(android.view.ViewAssistStructure);
     method public abstract void onScrollChanged(int, int, int, int);
     method public abstract void onSizeChanged(int, int, int, int);
     method public abstract void onStartTemporaryDetach();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 51152af..b43c462 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -30,6 +30,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -55,12 +56,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import libcore.io.IoUtils;
+
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.SizedInputStream;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -234,6 +235,10 @@
             return runForceDexOpt();
         }
 
+        if ("move".equals(op)) {
+            return runMove();
+        }
+
         try {
             if (args.length == 1) {
                 if (args[0].equalsIgnoreCase("-l")) {
@@ -1278,6 +1283,51 @@
         }
     }
 
+    class LocalPackageMoveObserver extends IPackageMoveObserver.Stub {
+        boolean finished;
+        int returnCode;
+
+        @Override
+        public void packageMoved(String packageName, int returnCode) throws RemoteException {
+            synchronized (this) {
+                this.finished = true;
+                this.returnCode = returnCode;
+                notifyAll();
+            }
+        }
+    }
+
+    public int runMove() {
+        final String packageName = nextArg();
+        String volumeUuid = nextArg();
+        if ("internal".equals(volumeUuid)) {
+            volumeUuid = null;
+        }
+
+        final LocalPackageMoveObserver obs = new LocalPackageMoveObserver();
+        try {
+            mPm.movePackageAndData(packageName, volumeUuid, obs);
+
+            synchronized (obs) {
+                while (!obs.finished) {
+                    try {
+                        obs.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) {
+                    System.out.println("Success");
+                    return 0;
+                } else {
+                    System.err.println("Failure [" + obs.returnCode + "]");
+                    return 1;
+                }
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     private int runUninstall() throws RemoteException {
         int flags = 0;
         int userId = UserHandle.USER_ALL;
@@ -1335,7 +1385,8 @@
         }
 
         final LocalIntentReceiver receiver = new LocalIntentReceiver();
-        mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);
+        mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
+                receiver.getIntentSender(), userId);
 
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
@@ -1820,6 +1871,7 @@
         System.err.println("       pm install-abandon SESSION_ID");
         System.err.println("       pm uninstall [-k] [--user USER_ID] PACKAGE");
         System.err.println("       pm set-installer PACKAGE INSTALLER");
+        System.err.println("       pm move PACKAGE [internal|UUID]");
         System.err.println("       pm clear [--user USER_ID] PACKAGE");
         System.err.println("       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT");
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index be7287f..f63d13c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2452,6 +2452,15 @@
             reply.writeNoException();
             return true;
         }
+
+        case UPDATE_LOCK_TASK_PACKAGES_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int userId = data.readInt();
+            String[] packages = data.readStringArray();
+            updateLockTaskPackages(userId, packages);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5687,5 +5696,18 @@
         reply.recycle();
     }
 
+    @Override
+    public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(userId);
+        data.writeStringArray(packages);
+        mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6ec5457..10dcd85 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,19 +62,19 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
-import android.os.SystemProperties;
+
+import dalvik.system.VMRuntime;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.UserIcons;
 
-import dalvik.system.VMRuntime;
-
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
@@ -1363,7 +1363,17 @@
         try {
             mPM.movePackage(packageName, observer, flags);
         } catch (RemoteException e) {
-            // Should never happen!
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void movePackageAndData(String packageName, String volumeUuid,
+            IPackageMoveObserver observer) {
+        try {
+            mPM.movePackageAndData(packageName, volumeUuid, observer);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e20b0da..4a1d6ff 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -488,6 +488,7 @@
 
     public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
             throws RemoteException;
+    public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
 
     /*
      * Private non-Binder interfaces
@@ -823,4 +824,5 @@
     int SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+287;
     int DUMP_HEAP_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+288;
     int SET_VOICE_KEEP_AWAKE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+289;
+    int UPDATE_LOCK_TASK_PACKAGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+290;
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 514802e9..eea47b7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2908,6 +2908,24 @@
     /** {@hide} */
     public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
 
+    /**
+     * Activity Action: Process a piece of text.
+     * <p>Input: {@link #EXTRA_PROCESS_TEXT} contains the text to be processed.
+     * {@link #EXTRA_PROCESS_TEXT_READONLY} states if the resulting text will be read-only.</p>
+     * <p>Output: {@link #EXTRA_PROCESS_TEXT} contains the processed text.</p>
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PROCESS_TEXT = "android.intent.action.PROCESS_TEXT";
+    /**
+     * The name of the extra used to define the text to be processed.
+     */
+    public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
+    /**
+     * The name of the extra used to define if the processed text will be used as read-only.
+     */
+    public static final String EXTRA_PROCESS_TEXT_READONLY =
+            "android.intent.extra.PROCESS_TEXT_READONLY";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8567c01..5bdb7bb 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -459,6 +459,8 @@
     public int largestWidthLimitDp = 0;
 
     /** {@hide} */
+    public String volumeUuid;
+    /** {@hide} */
     public String scanSourceDir;
     /** {@hide} */
     public String scanPublicSourceDir;
@@ -726,6 +728,7 @@
         requiresSmallestWidthDp = orig.requiresSmallestWidthDp;
         compatibleWidthLimitDp = orig.compatibleWidthLimitDp;
         largestWidthLimitDp = orig.largestWidthLimitDp;
+        volumeUuid = orig.volumeUuid;
         scanSourceDir = orig.scanSourceDir;
         scanPublicSourceDir = orig.scanPublicSourceDir;
         sourceDir = orig.sourceDir;
@@ -778,6 +781,7 @@
         dest.writeInt(requiresSmallestWidthDp);
         dest.writeInt(compatibleWidthLimitDp);
         dest.writeInt(largestWidthLimitDp);
+        dest.writeString(volumeUuid);
         dest.writeString(scanSourceDir);
         dest.writeString(scanPublicSourceDir);
         dest.writeString(sourceDir);
@@ -829,6 +833,7 @@
         requiresSmallestWidthDp = source.readInt();
         compatibleWidthLimitDp = source.readInt();
         largestWidthLimitDp = source.readInt();
+        volumeUuid = source.readString();
         scanSourceDir = source.readString();
         scanPublicSourceDir = source.readString();
         sourceDir = source.readString();
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index ba62cd6..154ff85 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -44,7 +44,8 @@
     void registerCallback(IPackageInstallerCallback callback, int userId);
     void unregisterCallback(IPackageInstallerCallback callback);
 
-    void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);
+    void uninstall(String packageName, String callerPackageName, int flags,
+            in IntentSender statusReceiver, int userId);
 
     void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 649bb47..eed0df5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -432,7 +432,8 @@
     PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
 
     void movePackage(String packageName, IPackageMoveObserver observer, int flags);
-    
+    void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);
+
     boolean addPermissionAsync(in PermissionInfo info);
 
     boolean setInstallLocation(int loc);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 80efd0b..b7ee82d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -423,7 +423,7 @@
      */
     public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
         try {
-            mInstaller.uninstall(packageName, 0, statusReceiver, mUserId);
+            mInstaller.uninstall(packageName, mInstallerPackageName, 0, statusReceiver, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -887,6 +887,8 @@
         public Uri referrerUri;
         /** {@hide} */
         public String abiOverride;
+        /** {@hide} */
+        public String volumeUuid;
 
         /**
          * Construct parameters for a new package install session.
@@ -911,6 +913,7 @@
             originatingUri = source.readParcelable(null);
             referrerUri = source.readParcelable(null);
             abiOverride = source.readString();
+            volumeUuid = source.readString();
         }
 
         /**
@@ -1008,6 +1011,7 @@
             pw.printPair("originatingUri", originatingUri);
             pw.printPair("referrerUri", referrerUri);
             pw.printPair("abiOverride", abiOverride);
+            pw.printPair("volumeUuid", volumeUuid);
             pw.println();
         }
 
@@ -1028,6 +1032,7 @@
             dest.writeParcelable(originatingUri, flags);
             dest.writeParcelable(referrerUri, flags);
             dest.writeString(abiOverride);
+            dest.writeString(volumeUuid);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 46d6ffb3..4c99d09 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -43,7 +43,9 @@
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
 import android.util.AndroidException;
+
 import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
@@ -339,15 +341,17 @@
     public static final int INSTALL_ALLOW_TEST = 0x00000004;
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that this
-     * package has to be installed on the sdcard.
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
+     *
      * @hide
      */
     public static final int INSTALL_EXTERNAL = 0x00000008;
 
     /**
      * Flag parameter for {@link #installPackage} to indicate that this package
-     * has to be installed on the sdcard.
+     * must be installed to internal storage.
+     *
      * @hide
      */
     public static final int INSTALL_INTERNAL = 0x00000010;
@@ -4099,8 +4103,12 @@
      *
      * @hide
      */
-    public abstract void movePackage(
-            String packageName, IPackageMoveObserver observer, int flags);
+    @Deprecated
+    public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
+
+    /** {@hide} */
+    public abstract void movePackageAndData(String packageName, String volumeUuid,
+            IPackageMoveObserver observer);
 
     /**
      * Returns the device identity that verifiers can use to associate their scheme to a particular
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f4ad434..c1e6a4d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -604,7 +604,7 @@
     public final static int PARSE_MUST_BE_APK = 1<<2;
     public final static int PARSE_IGNORE_PROCESSES = 1<<3;
     public final static int PARSE_FORWARD_LOCK = 1<<4;
-    public final static int PARSE_ON_SDCARD = 1<<5;
+    public final static int PARSE_EXTERNAL_STORAGE = 1<<5;
     public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
     public final static int PARSE_IS_PRIVILEGED = 1<<7;
     public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
@@ -1408,7 +1408,7 @@
         }
 
         /* Set the global "on SD card" flag */
-        if ((flags & PARSE_ON_SDCARD) != 0) {
+        if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
         }
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index b5eeb30..bc6d4ce 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1284,14 +1284,12 @@
 
     /**
      * Set the locale. This is the preferred way for setting up the locale (instead of using the
-     * direct accessor). This will also set the userLocale and layout direction according to
-     * the locale.
+     * direct accessor). This will also set the layout direction according to the locale.
      *
      * @param loc The locale. Can be null.
      */
     public void setLocale(Locale loc) {
         locale = loc;
-        userSetLocale = true;
         setLayoutDirection(locale);
     }
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3051926..508fdee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -269,15 +269,6 @@
         public abstract long getTotalTimeLocked(long elapsedRealtimeUs, int which);
 
         /**
-         * Returns the total time in microseconds associated with this Timer since the
-         * 'mark' was last set.
-         *
-         * @param elapsedRealtimeUs current elapsed realtime of system in microseconds
-         * @return a time in microseconds
-         */
-        public abstract long getTimeSinceMarkLocked(long elapsedRealtimeUs);
-
-        /**
          * Temporary for debugging.
          */
         public abstract void logState(Printer pw, String prefix);
@@ -341,17 +332,7 @@
          * @return a Map from Strings to Uid.Pkg objects.
          */
         public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
-
-        /**
-         * Returns the time in milliseconds that this app kept the WiFi controller in the
-         * specified state <code>type</code>.
-         * @param type one of {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, or
-         *             {@link #CONTROLLER_TX_TIME}.
-         * @param which one of {@link #STATS_CURRENT}, {@link #STATS_SINCE_CHARGED}, or
-         *              {@link #STATS_SINCE_UNPLUGGED}.
-         */
-        public abstract long getWifiControllerActivity(int type, int which);
-
+        
         /**
          * {@hide}
          */
@@ -1933,6 +1914,7 @@
     public static final int NETWORK_MOBILE_TX_DATA = 1;
     public static final int NETWORK_WIFI_RX_DATA = 2;
     public static final int NETWORK_WIFI_TX_DATA = 3;
+
     public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_TX_DATA + 1;
 
     public abstract long getNetworkActivityBytes(int type, int which);
@@ -1941,25 +1923,10 @@
     public static final int CONTROLLER_IDLE_TIME = 0;
     public static final int CONTROLLER_RX_TIME = 1;
     public static final int CONTROLLER_TX_TIME = 2;
-    public static final int CONTROLLER_POWER_DRAIN = 3;
-    public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_POWER_DRAIN + 1;
+    public static final int CONTROLLER_ENERGY = 3;
+    public static final int NUM_CONTROLLER_ACTIVITY_TYPES = CONTROLLER_ENERGY + 1;
 
-    /**
-     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
-     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
-     * respective state.
-     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
-     * milli-ampere-milliseconds (mAms).
-     */
     public abstract long getBluetoothControllerActivity(int type, int which);
-
-    /**
-     * For {@link #CONTROLLER_IDLE_TIME}, {@link #CONTROLLER_RX_TIME}, and
-     * {@link #CONTROLLER_TX_TIME}, returns the time spent (in milliseconds) in the
-     * respective state.
-     * For {@link #CONTROLLER_POWER_DRAIN}, returns the power used by the controller in
-     * milli-ampere-milliseconds (mAms).
-     */
     public abstract long getWifiControllerActivity(int type, int which);
 
     /**
@@ -2651,7 +2618,7 @@
                         label = "???";
                 }
                 dumpLine(pw, uid, category, POWER_USE_ITEM_DATA, label,
-                        BatteryStatsHelper.makemAh(bs.totalPowerMah));
+                        BatteryStatsHelper.makemAh(bs.value));
             }
         }
 
@@ -3297,13 +3264,6 @@
 
         sb.setLength(0);
         sb.append(prefix);
-        sb.append("  WiFi Energy use: ").append(BatteryStatsHelper.makemAh(
-                getWifiControllerActivity(CONTROLLER_POWER_DRAIN, which) / (double)(1000*60*60)));
-        sb.append(" mAh");
-        pw.println(sb.toString());
-
-        sb.setLength(0);
-        sb.append(prefix);
                 sb.append("  Bluetooth on: "); formatTimeMs(sb, bluetoothOnTime / 1000);
                 sb.append("("); sb.append(formatRatioLocked(bluetoothOnTime, whichBatteryRealtime));
                 sb.append(")");
@@ -3416,48 +3376,48 @@
                 final BatterySipper bs = sippers.get(i);
                 switch (bs.drainType) {
                     case IDLE:
-                        pw.print(prefix); pw.print("    Idle: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Idle: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case CELL:
-                        pw.print(prefix); pw.print("    Cell standby: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Cell standby: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case PHONE:
-                        pw.print(prefix); pw.print("    Phone calls: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Phone calls: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case WIFI:
-                        pw.print(prefix); pw.print("    Wifi: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Wifi: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case BLUETOOTH:
-                        pw.print(prefix); pw.print("    Bluetooth: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Bluetooth: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case SCREEN:
-                        pw.print(prefix); pw.print("    Screen: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Screen: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case FLASHLIGHT:
-                        pw.print(prefix); pw.print("    Flashlight: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Flashlight: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case APP:
                         pw.print(prefix); pw.print("    Uid ");
                         UserHandle.formatUid(pw, bs.uidObj.getUid());
-                        pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
+                        pw.print(": "); printmAh(pw, bs.value); pw.println();
                         break;
                     case USER:
                         pw.print(prefix); pw.print("    User "); pw.print(bs.userId);
-                        pw.print(": "); printmAh(pw, bs.totalPowerMah); pw.println();
+                        pw.print(": "); printmAh(pw, bs.value); pw.println();
                         break;
                     case UNACCOUNTED:
-                        pw.print(prefix); pw.print("    Unaccounted: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Unaccounted: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                     case OVERCOUNTED:
-                        pw.print(prefix); pw.print("    Over-counted: "); printmAh(pw, bs.totalPowerMah);
+                        pw.print(prefix); pw.print("    Over-counted: "); printmAh(pw, bs.value);
                         pw.println();
                         break;
                 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 486f9b4..5a5d8b7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -43,6 +43,7 @@
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -231,8 +232,7 @@
         mLooper = looper;
         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
         if (mMountService == null) {
-            Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
-            return;
+            throw new IllegalStateException("Failed to find running mount service");
         }
     }
 
@@ -442,6 +442,42 @@
     }
 
     /** {@hide} */
+    public @Nullable DiskInfo findDiskById(String id) {
+        Preconditions.checkNotNull(id);
+        // TODO; go directly to service to make this faster
+        for (DiskInfo disk : getDisks()) {
+            if (Objects.equals(disk.id, id)) {
+                return disk;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    public @Nullable VolumeInfo findVolumeById(String id) {
+        Preconditions.checkNotNull(id);
+        // TODO; go directly to service to make this faster
+        for (VolumeInfo vol : getVolumes()) {
+            if (Objects.equals(vol.id, id)) {
+                return vol;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
+    public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) {
+        Preconditions.checkNotNull(fsUuid);
+        // TODO; go directly to service to make this faster
+        for (VolumeInfo vol : getVolumes()) {
+            if (Objects.equals(vol.fsUuid, fsUuid)) {
+                return vol;
+            }
+        }
+        return null;
+    }
+
+    /** {@hide} */
     public @NonNull List<VolumeInfo> getVolumes() {
         try {
             return Arrays.asList(mMountService.getVolumes());
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index c8c2f65..2dc0361 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -44,6 +44,7 @@
  * @hide
  */
 public class VolumeInfo implements Parcelable {
+    /** Real volume representing internal emulated storage */
     public static final String ID_EMULATED_INTERNAL = "emulated";
 
     public static final int TYPE_PUBLIC = 0;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 5afbd6d..7565654b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -226,6 +226,35 @@
     public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
 
     /**
+     * The name of the Intent action used to indicate that a camera launch might be imminent. This
+     * broadcast should be targeted to the package that is receiving
+     * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or
+     * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE}, depending on the context. If such
+     * intent would launch the resolver activity, this broadcast should not be sent at all.
+     * <p>
+     * A receiver of this broadcast should do the absolute minimum amount of work to initialize the
+     * camera in order to reduce startup time in likely case that shortly after an actual camera
+     * launch intent would be sent.
+     * <p>
+     * In case the actual intent will not be fired, the target package will receive
+     * {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN}. However, it is recommended that the receiver
+     * also implements a timeout to close the camera after receiving this intent, as there is no
+     * guarantee that {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN} will be delivered.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM";
+
+    /**
+     * The name of the Intent action used to indicate that an imminent camera launch has been
+     * cancelled by the user. This broadcast should be targeted to the package that has received
+     * {@link #ACTION_STILL_IMAGE_CAMERA_PREWARM}.
+     * <p>
+     * A receiver of this broadcast should close the camera immediately.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN";
+
+    /**
      * The name of the Intent action used to launch a camera in still image mode
      * for use when the device is secured (e.g. with a pin, password, pattern,
      * or face unlock). Applications responding to this intent must not expose
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e94a312..5e2accd 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -171,6 +171,10 @@
     public static final int KM_KEY_FORMAT_PKCS12 = 2;
     public static final int KM_KEY_FORMAT_RAW = 3;
 
+    // User authenticators.
+    public static final int HW_AUTH_PASSWORD = 1 << 0;
+    public static final int HW_AUTH_FINGERPRINT = 1 << 1;
+
     // Error codes.
     public static final int KM_ERROR_OK = 0;
     public static final int KM_ERROR_ROOT_OF_TRUST_ALREADY_SET = -1;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 384bd2c..b6f1e3b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8400,7 +8400,7 @@
                     return true;
                 }
             } break;
-            case R.id.accessibility_action_show_on_screen: {
+            case R.id.accessibilityActionShowOnScreen: {
                 if (mAttachInfo != null) {
                     final Rect r = mAttachInfo.mTmpInvalRect;
                     getDrawingRect(r);
@@ -16918,7 +16918,7 @@
     @Nullable
     public ColorStateList getForegroundTintList() {
         return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
-                ? mBackgroundTint.mTintList : null;
+                ? mForegroundInfo.mTintInfo.mTintList : null;
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index ec527d5..0736ed8 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -266,7 +266,8 @@
      * Action to set the selection. Performing this action with no arguments
      * clears the selection.
      * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
+     * <strong>Arguments:</strong>
+     * {@link #ACTION_ARGUMENT_SELECTION_START_INT},
      * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
      * <strong>Example:</strong>
      * <code><pre><p>
@@ -302,7 +303,8 @@
      * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
      * cursor at the end of text.
      * <p>
-     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
+     * <strong>Arguments:</strong>
+     * {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
      * <strong>Example:</strong>
      * <code><pre><p>
      *   Bundle arguments = new Bundle();
@@ -326,12 +328,15 @@
      * Argument for which movement granularity to be used when traversing the node text.
      * <p>
      * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
-     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
+     *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
+     * </ul>
      * </p>
      *
-     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
-     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
      */
     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
             "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
@@ -340,12 +345,15 @@
      * Argument for which HTML element to get moving to the next/previous HTML element.
      * <p>
      * <strong>Type:</strong> String<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
-     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_NEXT_HTML_ELEMENT}</li>
+     *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT}</li>
+     * </ul>
      * </p>
      *
-     * @see #ACTION_NEXT_HTML_ELEMENT
-     * @see #ACTION_PREVIOUS_HTML_ELEMENT
+     * @see AccessibilityAction#ACTION_NEXT_HTML_ELEMENT
+     * @see AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT
      */
     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
             "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
@@ -355,12 +363,14 @@
      * or to move it otherwise.
      * <p>
      * <strong>Type:</strong> boolean<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
-     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
-     * </p>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
+     *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
+     * </ul>
      *
-     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
-     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
      */
     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
@@ -369,10 +379,12 @@
      * Argument for specifying the selection start.
      * <p>
      * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
-     * </p>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
+     * </ul>
      *
-     * @see #ACTION_SET_SELECTION
+     * @see AccessibilityAction#ACTION_SET_SELECTION
      */
     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
             "ACTION_ARGUMENT_SELECTION_START_INT";
@@ -381,26 +393,58 @@
      * Argument for specifying the selection end.
      * <p>
      * <strong>Type:</strong> int<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
-     * </p>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
+     * </ul>
      *
-     * @see #ACTION_SET_SELECTION
+     * @see AccessibilityAction#ACTION_SET_SELECTION
      */
     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
             "ACTION_ARGUMENT_SELECTION_END_INT";
 
     /**
-     * Argument for specifying the text content to set
+     * Argument for specifying the text content to set.
      * <p>
      * <strong>Type:</strong> CharSequence<br>
-     * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
-     * </p>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_SET_TEXT}</li>
+     * </ul>
      *
-     * @see #ACTION_SET_TEXT
+     * @see AccessibilityAction#ACTION_SET_TEXT
      */
     public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
 
+    /**
+     * Argument for specifying the collection row to make visible on screen.
+     * <p>
+     * <strong>Type:</strong> int<br>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
+     * </ul>
+     *
+     * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
+     */
+    public static final String ACTION_ARGUMENT_ROW_INT =
+            "android.view.accessibility.action.ARGUMENT_ROW_INT";
+
+    /**
+     * Argument for specifying the collection column to make visible on screen.
+     * <p>
+     * <strong>Type:</strong> int<br>
+     * <strong>Actions:</strong>
+     * <ul>
+     *     <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
+     * </ul>
+     *
+     * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
+     */
+    public static final String ACTION_ARGUMENT_COLUMN_INT =
+            "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+
     // Focus types
 
     /**
@@ -539,7 +583,7 @@
     // Housekeeping.
     private static final int MAX_POOL_SIZE = 50;
     private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
-            new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE);
+            new SynchronizedPool<>(MAX_POOL_SIZE);
 
     private boolean mSealed;
 
@@ -970,7 +1014,7 @@
         }
 
         if (mActions == null) {
-            mActions = new ArrayList<AccessibilityAction>();
+            mActions = new ArrayList<>();
         }
 
         mActions.remove(action);
@@ -3411,9 +3455,24 @@
          * @see View#requestRectangleOnScreen(Rect)
          */
         public static final AccessibilityAction ACTION_SHOW_ON_SCREEN =
-                new AccessibilityAction(R.id.accessibility_action_show_on_screen, null);
+                new AccessibilityAction(R.id.accessibilityActionShowOnScreen, null);
 
-        private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
+        /**
+         * Action that scrolls the node to make the specified collection
+         * position visible on screen.
+         * <p>
+         * <strong>Arguments:</strong>
+         * <ul>
+         *     <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li>
+         *     <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li>
+         * <ul>
+         *
+         * @see AccessibilityNodeInfo#getCollectionInfo()
+         */
+        public static final AccessibilityAction ACTION_SCROLL_TO_POSITION =
+                new AccessibilityAction(R.id.accessibilityActionScrollToPosition, null);
+
+        private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
         static {
             sStandardActions.add(ACTION_FOCUS);
             sStandardActions.add(ACTION_CLEAR_FOCUS);
@@ -3438,6 +3497,7 @@
             sStandardActions.add(ACTION_DISMISS);
             sStandardActions.add(ACTION_SET_TEXT);
             sStandardActions.add(ACTION_SHOW_ON_SCREEN);
+            sStandardActions.add(ACTION_SCROLL_TO_POSITION);
         }
 
         private final int mActionId;
@@ -3658,7 +3718,7 @@
         private static final int MAX_POOL_SIZE = 20;
 
         private static final SynchronizedPool<CollectionInfo> sPool =
-                new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
+                new SynchronizedPool<>(MAX_POOL_SIZE);
 
         private int mRowCount;
         private int mColumnCount;
@@ -3804,7 +3864,7 @@
         private static final int MAX_POOL_SIZE = 20;
 
         private static final SynchronizedPool<CollectionItemInfo> sPool =
-                new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
+                new SynchronizedPool<>(MAX_POOL_SIZE);
 
         /**
          * Obtains a pooled instance that is a clone of another one.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index aa77d5e..7ab5aaa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -40,6 +40,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewAssistStructure;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -2424,6 +2425,11 @@
         return WebView.class.getName();
     }
 
+    @Override
+    public void onProvideVirtualAssistStructure(ViewAssistStructure structure) {
+        mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure);
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index fa2ce1b..d5787de 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -32,6 +32,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewAssistStructure;
 import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -298,6 +299,8 @@
     interface ViewDelegate {
         public boolean shouldDelayChildPressedState();
 
+        public void onProvideVirtualAssistStructure(ViewAssistStructure structure);
+
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
 
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 168066a..e7b6238 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -61,6 +61,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
@@ -1504,11 +1505,11 @@
         super.onInitializeAccessibilityNodeInfoInternal(info);
         if (isEnabled()) {
             if (canScrollUp()) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
                 info.setScrollable(true);
             }
             if (canScrollDown()) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
                 info.setScrollable(true);
             }
         }
@@ -2502,18 +2503,18 @@
 
         if (position == getSelectedItemPosition()) {
             info.setSelected(true);
-            info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+            info.addAction(AccessibilityAction.ACTION_CLEAR_SELECTION);
         } else {
-            info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+            info.addAction(AccessibilityAction.ACTION_SELECT);
         }
 
         if (isClickable()) {
-            info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+            info.addAction(AccessibilityAction.ACTION_CLICK);
             info.setClickable(true);
         }
 
         if (isLongClickable()) {
-            info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
+            info.addAction(AccessibilityAction.ACTION_LONG_CLICK);
             info.setLongClickable(true);
         }
     }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index c6e3dc8..be0ca71 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.MathUtils;
@@ -32,12 +33,15 @@
 import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
 import android.view.animation.GridLayoutAnimationController;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.R;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -117,7 +121,7 @@
     }
 
     public GridView(Context context, AttributeSet attrs) {
-        this(context, attrs, com.android.internal.R.attr.gridViewStyle);
+        this(context, attrs, R.attr.gridViewStyle);
     }
 
     public GridView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -128,30 +132,30 @@
         super(context, attrs, defStyleAttr, defStyleRes);
 
         final TypedArray a = context.obtainStyledAttributes(
-                attrs, com.android.internal.R.styleable.GridView, defStyleAttr, defStyleRes);
+                attrs, R.styleable.GridView, defStyleAttr, defStyleRes);
 
         int hSpacing = a.getDimensionPixelOffset(
-                com.android.internal.R.styleable.GridView_horizontalSpacing, 0);
+                R.styleable.GridView_horizontalSpacing, 0);
         setHorizontalSpacing(hSpacing);
 
         int vSpacing = a.getDimensionPixelOffset(
-                com.android.internal.R.styleable.GridView_verticalSpacing, 0);
+                R.styleable.GridView_verticalSpacing, 0);
         setVerticalSpacing(vSpacing);
 
-        int index = a.getInt(com.android.internal.R.styleable.GridView_stretchMode, STRETCH_COLUMN_WIDTH);
+        int index = a.getInt(R.styleable.GridView_stretchMode, STRETCH_COLUMN_WIDTH);
         if (index >= 0) {
             setStretchMode(index);
         }
 
-        int columnWidth = a.getDimensionPixelOffset(com.android.internal.R.styleable.GridView_columnWidth, -1);
+        int columnWidth = a.getDimensionPixelOffset(R.styleable.GridView_columnWidth, -1);
         if (columnWidth > 0) {
             setColumnWidth(columnWidth);
         }
 
-        int numColumns = a.getInt(com.android.internal.R.styleable.GridView_numColumns, 1);
+        int numColumns = a.getInt(R.styleable.GridView_numColumns, 1);
         setNumColumns(numColumns);
 
-        index = a.getInt(com.android.internal.R.styleable.GridView_gravity, -1);
+        index = a.getInt(R.styleable.GridView_gravity, -1);
         if (index >= 0) {
             setGravity(index);
         }
@@ -2356,6 +2360,36 @@
         final CollectionInfo collectionInfo = CollectionInfo.obtain(
                 rowsCount, columnsCount, false, selectionMode);
         info.setCollectionInfo(collectionInfo);
+
+        if (columnsCount > 0 || rowsCount > 0) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_TO_POSITION);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+
+        switch (action) {
+            case R.id.accessibilityActionScrollToPosition: {
+                // GridView only supports scrolling in one direction, so we can
+                // ignore the column argument.
+                final int numColumns = getNumColumns();
+                final int row = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, -1);
+                final int position = Math.min(row * numColumns, getCount() - 1);
+                if (row >= 0) {
+                    // The accessibility service gets data asynchronously, so
+                    // we'll be a little lenient by clamping the last position.
+                    smoothScrollToPosition(position);
+                    return true;
+                }
+            } break;
+        }
+
+        return false;
     }
 
     @Override
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index ee2c055..def5929 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.os.Bundle;
 import android.os.Trace;
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
@@ -42,6 +43,7 @@
 import android.view.ViewParent;
 import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
@@ -3893,6 +3895,33 @@
         final CollectionInfo collectionInfo = CollectionInfo.obtain(
                 rowsCount, 1, false, selectionMode);
         info.setCollectionInfo(collectionInfo);
+
+        if (rowsCount > 0) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_TO_POSITION);
+        }
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+
+        switch (action) {
+            case R.id.accessibilityActionScrollToPosition: {
+                final int row = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_ROW_INT, -1);
+                final int position = Math.min(row, getCount() - 1);
+                if (row >= 0) {
+                    // The accessibility service gets data asynchronously, so
+                    // we'll be a little lenient by clamping the last position.
+                    smoothScrollToPosition(position);
+                    return true;
+                }
+            } break;
+        }
+
+        return false;
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 1746bed..bea4ece 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -109,7 +109,6 @@
     void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
     void noteWifiMulticastEnabledFromSource(in WorkSource ws);
     void noteWifiMulticastDisabledFromSource(in WorkSource ws);
-    void noteWifiRadioPowerState(int powerState, long timestampNs);
     void noteNetworkInterfaceType(String iface, int type);
     void noteNetworkStatsEnabled();
     void noteDeviceIdleMode(boolean enabled, boolean fromActive, boolean fromMotion);
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 1f0bb76..4efefa9 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -246,9 +246,8 @@
             IActivityManager am = ActivityManagerNative.getDefault();
             Configuration config = am.getConfiguration();
 
-            // Will set userSetLocale to indicate this isn't some passing default - the user
-            // wants this remembered
             config.setLocale(locale);
+            config.userSetLocale = true;
 
             am.updateConfiguration(config);
             // Trigger the dirty bit for the Settings Provider.
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 7bdb4be..255f1fd 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -25,15 +25,16 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageParser.PackageLite;
 import android.os.Environment;
-import android.os.Environment.UserEnvironment;
 import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageResultCode;
+import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
+import android.util.ArraySet;
 import android.util.Log;
 
 import libcore.io.IoUtils;
@@ -335,6 +336,94 @@
 
     /**
      * Given a requested {@link PackageInfo#installLocation} and calculated
+     * install size, pick the actual volume to install the app. Only considers
+     * internal and private volumes, and prefers to keep an existing package on
+     * its current volume.
+     *
+     * @return the {@link VolumeInfo#fsUuid} to install onto, or {@code null}
+     *         for internal storage.
+     */
+    public static String resolveInstallVolume(Context context, String packageName,
+            int installLocation, long sizeBytes) throws IOException {
+        // TODO: handle existing apps installed in ASEC; currently assumes
+        // they'll end up back on internal storage
+        ApplicationInfo existingInfo = null;
+        try {
+            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+        } catch (NameNotFoundException ignored) {
+        }
+
+        final StorageManager storageManager = context.getSystemService(StorageManager.class);
+        final boolean fitsOnInternal = fitsOnInternal(context, sizeBytes);
+
+        final ArraySet<String> allCandidates = new ArraySet<>();
+        VolumeInfo bestCandidate = null;
+        long bestCandidateAvailBytes = Long.MIN_VALUE;
+        for (VolumeInfo vol : storageManager.getVolumes()) {
+            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.state == VolumeInfo.STATE_MOUNTED) {
+                final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
+                if (availBytes >= sizeBytes) {
+                    allCandidates.add(vol.fsUuid);
+                }
+                if (availBytes >= bestCandidateAvailBytes) {
+                    bestCandidate = vol;
+                    bestCandidateAvailBytes = availBytes;
+                }
+            }
+        }
+
+        // System apps always forced to internal storage
+        if (existingInfo != null && existingInfo.isSystemApp()) {
+            installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+        }
+
+        // If app expresses strong desire for internal space, honor it
+        if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+            if (fitsOnInternal) {
+                return null;
+            } else {
+                throw new IOException("Requested internal only, but not enough space");
+            }
+        }
+
+        // If app already exists somewhere, prefer to stay on that volume
+        if (existingInfo != null) {
+            if (existingInfo.volumeUuid == null && fitsOnInternal) {
+                return null;
+            }
+            if (allCandidates.contains(existingInfo.volumeUuid)) {
+                return existingInfo.volumeUuid;
+            }
+        }
+
+        // We're left with either preferring external or auto, so just pick
+        // volume with most space
+        if (bestCandidate != null) {
+            return bestCandidate.fsUuid;
+        } else if (fitsOnInternal) {
+            return null;
+        } else {
+            throw new IOException("No special requests, but no room anywhere");
+        }
+    }
+
+    public static boolean fitsOnInternal(Context context, long sizeBytes) {
+        final StorageManager storage = context.getSystemService(StorageManager.class);
+        final File target = Environment.getDataDirectory();
+        return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+    }
+
+    public static boolean fitsOnExternal(Context context, long sizeBytes) {
+        final StorageManager storage = context.getSystemService(StorageManager.class);
+        final StorageVolume primary = storage.getPrimaryVolume();
+        return (sizeBytes > 0) && !primary.isEmulated()
+                && Environment.MEDIA_MOUNTED.equals(primary.getState())
+                && sizeBytes <= storage.getStorageBytesUntilLow(primary.getPathFile());
+    }
+
+    /**
+     * Given a requested {@link PackageInfo#installLocation} and calculated
      * install size, pick the actual location to install the app.
      */
     public static int resolveInstallLocation(Context context, String packageName,
@@ -363,6 +452,7 @@
         } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
             // When app is already installed, prefer same medium
             if (existingInfo != null) {
+                // TODO: distinguish if this is external ASEC
                 if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                     prefer = RECOMMEND_INSTALL_EXTERNAL;
                 } else {
@@ -377,30 +467,21 @@
             checkBoth = false;
         }
 
-        final boolean emulated = Environment.isExternalStorageEmulated();
-        final StorageManager storage = StorageManager.from(context);
-
         boolean fitsOnInternal = false;
         if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
-            final File target = Environment.getDataDirectory();
-            fitsOnInternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
+            fitsOnInternal = fitsOnInternal(context, sizeBytes);
         }
 
         boolean fitsOnExternal = false;
-        if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
-            final File target = new UserEnvironment(UserHandle.USER_OWNER)
-                    .getExternalStorageDirectory();
-            // External is only an option when size is known
-            if (sizeBytes > 0) {
-                fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
-            }
+        if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
+            fitsOnExternal = fitsOnExternal(context, sizeBytes);
         }
 
         if (prefer == RECOMMEND_INSTALL_INTERNAL) {
             if (fitsOnInternal) {
                 return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
             }
-        } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
+        } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
             if (fitsOnExternal) {
                 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
             }
@@ -409,22 +490,12 @@
         if (checkBoth) {
             if (fitsOnInternal) {
                 return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
-            } else if (!emulated && fitsOnExternal) {
+            } else if (fitsOnExternal) {
                 return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
             }
         }
 
-        /*
-         * If they requested to be on the external media by default, return that
-         * the media was unavailable. Otherwise, indicate there was insufficient
-         * storage space available.
-         */
-        if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)
-                && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
-            return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
-        } else {
-            return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
-        }
+        return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
     }
 
     public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked,
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 056b0aa..4cd959f 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,25 +23,17 @@
 public class BatterySipper implements Comparable<BatterySipper> {
     public int userId;
     public Uid uidObj;
-    public double totalPowerMah;
+    public double value;
+    public double[] values;
     public DrainType drainType;
 
-    /**
-     * Generic usage time in milliseconds.
-     */
-    public long usageTimeMs;
-
-    /**
-     * Generic power usage in mAh.
-     */
-    public double usagePowerMah;
-
-    // Subsystem usage times.
-    public long cpuTimeMs;
-    public long gpsTimeMs;
-    public long wifiRunningTimeMs;
-    public long cpuFgTimeMs;
-    public long wakeLockTimeMs;
+    // Measured in milliseconds.
+    public long usageTime;
+    public long cpuTime;
+    public long gpsTime;
+    public long wifiRunningTime;
+    public long cpuFgTime;
+    public long wakeLockTime;
 
     public long mobileRxPackets;
     public long mobileTxPackets;
@@ -60,13 +52,12 @@
     public String packageWithHighestDrain;
 
     // Measured in mAh (milli-ampere per hour).
-    // These are included when summed.
-    public double wifiPowerMah;
-    public double cpuPowerMah;
-    public double wakeLockPowerMah;
-    public double mobileRadioPowerMah;
-    public double gpsPowerMah;
-    public double sensorPowerMah;
+    public double wifiPower;
+    public double cpuPower;
+    public double wakeLockPower;
+    public double mobileRadioPower;
+    public double gpsPower;
+    public double sensorPower;
 
     public enum DrainType {
         IDLE,
@@ -82,12 +73,17 @@
         OVERCOUNTED
     }
 
-    public BatterySipper(DrainType drainType, Uid uid, double value) {
-        this.totalPowerMah = value;
+    public BatterySipper(DrainType drainType, Uid uid, double[] values) {
+        this.values = values;
+        if (values != null) value = values[0];
         this.drainType = drainType;
         uidObj = uid;
     }
 
+    public double[] getValues() {
+        return values;
+    }
+
     public void computeMobilemspp() {
         long packets = mobileRxPackets+mobileTxPackets;
         mobilemspp = packets > 0 ? (mobileActive / (double)packets) : 0;
@@ -105,7 +101,7 @@
             }
         }
         // Return the flipped value because we want the items in descending order
-        return Double.compare(other.totalPowerMah, totalPowerMah);
+        return Double.compare(other.value, value);
     }
 
     /**
@@ -127,14 +123,11 @@
      * Add stats from other to this BatterySipper.
      */
     public void add(BatterySipper other) {
-        totalPowerMah += other.totalPowerMah;
-        usageTimeMs += other.usageTimeMs;
-        usagePowerMah += other.usagePowerMah;
-        cpuTimeMs += other.cpuTimeMs;
-        gpsTimeMs += other.gpsTimeMs;
-        wifiRunningTimeMs += other.wifiRunningTimeMs;
-        cpuFgTimeMs += other.cpuFgTimeMs;
-        wakeLockTimeMs += other.wakeLockTimeMs;
+        cpuTime += other.cpuTime;
+        gpsTime += other.gpsTime;
+        wifiRunningTime += other.wifiRunningTime;
+        cpuFgTime += other.cpuFgTime;
+        wakeLockTime += other.wakeLockTime;
         mobileRxPackets += other.mobileRxPackets;
         mobileTxPackets += other.mobileTxPackets;
         mobileActive += other.mobileActive;
@@ -145,20 +138,11 @@
         mobileTxBytes += other.mobileTxBytes;
         wifiRxBytes += other.wifiRxBytes;
         wifiTxBytes += other.wifiTxBytes;
-        wifiPowerMah += other.wifiPowerMah;
-        gpsPowerMah += other.gpsPowerMah;
-        cpuPowerMah += other.cpuPowerMah;
-        sensorPowerMah += other.sensorPowerMah;
-        mobileRadioPowerMah += other.mobileRadioPowerMah;
-        wakeLockPowerMah += other.wakeLockPowerMah;
-    }
-
-    /**
-     * Sum all the powers and store the value into `value`.
-     * @return the sum of all the power in this BatterySipper.
-     */
-    public double sumPower() {
-        return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah
-                + mobileRadioPowerMah + wakeLockPowerMah;
+        wifiPower += other.wifiPower;
+        gpsPower += other.gpsPower;
+        cpuPower += other.cpuPower;
+        sensorPower += other.sensorPower;
+        mobileRadioPower += other.mobileRadioPower;
+        wakeLockPower += other.wakeLockPower;
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 024b7c5..d3611bf 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -16,12 +16,17 @@
 
 package com.android.internal.os;
 
+import static android.os.BatteryStats.NETWORK_MOBILE_RX_DATA;
+import static android.os.BatteryStats.NETWORK_MOBILE_TX_DATA;
+import static android.os.BatteryStats.NETWORK_WIFI_RX_DATA;
+import static android.os.BatteryStats.NETWORK_WIFI_TX_DATA;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.Uid;
 import android.os.Bundle;
@@ -33,6 +38,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.SignalStrength;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
@@ -48,6 +54,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A helper class for retrieving the power usage information for all applications and services.
@@ -56,7 +63,8 @@
  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
  */
 public final class BatteryStatsHelper {
-    static final boolean DEBUG = false;
+
+    private static final boolean DEBUG = false;
 
     private static final String TAG = BatteryStatsHelper.class.getSimpleName();
 
@@ -73,24 +81,14 @@
     private Intent mBatteryBroadcast;
     private PowerProfile mPowerProfile;
 
-    /**
-     * List of apps using power.
-     */
-    private final List<BatterySipper> mUsageList = new ArrayList<>();
+    private final List<BatterySipper> mUsageList = new ArrayList<BatterySipper>();
+    private final List<BatterySipper> mWifiSippers = new ArrayList<BatterySipper>();
+    private final List<BatterySipper> mBluetoothSippers = new ArrayList<BatterySipper>();
+    private final SparseArray<List<BatterySipper>> mUserSippers
+            = new SparseArray<List<BatterySipper>>();
+    private final SparseArray<Double> mUserPower = new SparseArray<Double>();
 
-    /**
-     * List of apps using wifi power.
-     */
-    private final List<BatterySipper> mWifiSippers = new ArrayList<>();
-
-    /**
-     * List of apps using bluetooth power.
-     */
-    private final List<BatterySipper> mBluetoothSippers = new ArrayList<>();
-
-    private final SparseArray<List<BatterySipper>> mUserSippers = new SparseArray<>();
-
-    private final List<BatterySipper> mMobilemsppList = new ArrayList<>();
+    private final List<BatterySipper> mMobilemsppList = new ArrayList<BatterySipper>();
 
     private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
 
@@ -104,50 +102,29 @@
     long mChargeTimeRemaining;
 
     private long mStatsPeriod = 0;
-
-    // The largest entry by power.
     private double mMaxPower = 1;
-
-    // The largest real entry by power (not undercounted or overcounted).
     private double mMaxRealPower = 1;
-
-    // Total computed power.
     private double mComputedPower;
     private double mTotalPower;
+    private double mWifiPower;
+    private double mBluetoothPower;
     private double mMinDrainedPower;
     private double mMaxDrainedPower;
 
-    PowerCalculator mCpuPowerCalculator;
-    PowerCalculator mWakelockPowerCalculator;
-    MobileRadioPowerCalculator mMobileRadioPowerCalculator;
-    PowerCalculator mWifiPowerCalculator;
-    PowerCalculator mBluetoothPowerCalculator;
-    PowerCalculator mSensorPowerCalculator;
+    // How much the apps together have kept the mobile radio active.
+    private long mAppMobileActive;
 
-    public static boolean checkWifiOnly(Context context) {
-        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
-    }
-
-    public static boolean checkHasWifiPowerReporting(Context context, PowerProfile profile) {
-        WifiManager manager = context.getSystemService(WifiManager.class);
-        if (manager.isEnhancedPowerReportingSupported()) {
-            if (profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
-                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
-                    profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0) {
-                return true;
-            }
-        }
-        return false;
-    }
+    // How much the apps together have left WIFI running.
+    private long mAppWifiRunning;
 
     public BatteryStatsHelper(Context context) {
         this(context, true);
     }
 
     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast) {
-        this(context, collectBatteryBroadcast, checkWifiOnly(context));
+        mContext = context;
+        mCollectBatteryBroadcast = collectBatteryBroadcast;
+        mWifiOnly = checkWifiOnly(context);
     }
 
     public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) {
@@ -156,6 +133,12 @@
         mWifiOnly = wifiOnly;
     }
 
+    public static boolean checkWifiOnly(Context context) {
+        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+    }
+
     public void storeStatsHistoryInFile(String fname) {
         synchronized (sFileXfer) {
             File path = makeFilePath(mContext, fname);
@@ -277,7 +260,7 @@
      * Refreshes the power usage list.
      */
     public void refreshStats(int statsType, int asUser) {
-        SparseArray<UserHandle> users = new SparseArray<>(1);
+        SparseArray<UserHandle> users = new SparseArray<UserHandle>(1);
         users.put(asUser, new UserHandle(asUser));
         refreshStats(statsType, users);
     }
@@ -287,7 +270,7 @@
      */
     public void refreshStats(int statsType, List<UserHandle> asUsers) {
         final int n = asUsers.size();
-        SparseArray<UserHandle> users = new SparseArray<>(n);
+        SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);
         for (int i = 0; i < n; ++i) {
             UserHandle userHandle = asUsers.get(i);
             users.put(userHandle.getIdentifier(), userHandle);
@@ -312,52 +295,22 @@
         mMaxRealPower = 0;
         mComputedPower = 0;
         mTotalPower = 0;
+        mWifiPower = 0;
+        mBluetoothPower = 0;
+        mAppMobileActive = 0;
+        mAppWifiRunning = 0;
 
         mUsageList.clear();
         mWifiSippers.clear();
         mBluetoothSippers.clear();
         mUserSippers.clear();
+        mUserPower.clear();
         mMobilemsppList.clear();
 
         if (mStats == null) {
             return;
         }
 
-        if (mCpuPowerCalculator == null) {
-            mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
-        }
-        mCpuPowerCalculator.reset();
-
-        if (mWakelockPowerCalculator == null) {
-            mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
-        }
-        mWakelockPowerCalculator.reset();
-
-        if (mMobileRadioPowerCalculator == null) {
-            mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile, mStats);
-        }
-        mMobileRadioPowerCalculator.reset(mStats);
-
-        if (mWifiPowerCalculator == null) {
-            if (checkHasWifiPowerReporting(mContext, mPowerProfile)) {
-                mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
-            } else {
-                mWifiPowerCalculator = new WifiPowerEstimator(mPowerProfile);
-            }
-        }
-        mWifiPowerCalculator.reset();
-
-        if (mBluetoothPowerCalculator == null) {
-            mBluetoothPowerCalculator = new BluetoothPowerCalculator();
-        }
-        mBluetoothPowerCalculator.reset();
-
-        if (mSensorPowerCalculator == null) {
-            mSensorPowerCalculator = new SensorPowerCalculator(mPowerProfile,
-                    (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE));
-        }
-        mSensorPowerCalculator.reset();
-
         mStatsType = statsType;
         mRawUptime = rawUptimeUs;
         mRawRealtime = rawRealtimeUs;
@@ -405,113 +358,383 @@
         Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {
             @Override
             public int compare(BatterySipper lhs, BatterySipper rhs) {
-                return Double.compare(rhs.mobilemspp, lhs.mobilemspp);
+                if (lhs.mobilemspp < rhs.mobilemspp) {
+                    return 1;
+                } else if (lhs.mobilemspp > rhs.mobilemspp) {
+                    return -1;
+                }
+                return 0;
             }
         });
 
         processMiscUsage();
 
-        Collections.sort(mUsageList);
-
-        // At this point, we've sorted the list so we are guaranteed the max values are at the top.
-        // We have only added real powers so far.
-        if (!mUsageList.isEmpty()) {
-            mMaxRealPower = mMaxPower = mUsageList.get(0).totalPowerMah;
-            final int usageListCount = mUsageList.size();
-            for (int i = 0; i < usageListCount; i++) {
-                mComputedPower += mUsageList.get(i).totalPowerMah;
-            }
-        }
-
         if (DEBUG) {
             Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="
                     + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));
         }
-
         mTotalPower = mComputedPower;
         if (mStats.getLowDischargeAmountSinceCharge() > 1) {
             if (mMinDrainedPower > mComputedPower) {
                 double amount = mMinDrainedPower - mComputedPower;
                 mTotalPower = mMinDrainedPower;
-                BatterySipper bs = new BatterySipper(DrainType.UNACCOUNTED, null, amount);
-
-                // Insert the BatterySipper in its sorted position.
-                int index = Collections.binarySearch(mUsageList, bs);
-                if (index < 0) {
-                    index = -(index + 1);
-                }
-                mUsageList.add(index, bs);
-                mMaxPower = Math.max(mMaxPower, amount);
+                addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);
             } else if (mMaxDrainedPower < mComputedPower) {
                 double amount = mComputedPower - mMaxDrainedPower;
-
-                // Insert the BatterySipper in its sorted position.
-                BatterySipper bs = new BatterySipper(DrainType.OVERCOUNTED, null, amount);
-                int index = Collections.binarySearch(mUsageList, bs);
-                if (index < 0) {
-                    index = -(index + 1);
-                }
-                mUsageList.add(index, bs);
-                mMaxPower = Math.max(mMaxPower, amount);
+                addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);
             }
         }
+
+        Collections.sort(mUsageList);
     }
 
     private void processAppUsage(SparseArray<UserHandle> asUsers) {
         final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
+        final SensorManager sensorManager =
+                (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        final int which = mStatsType;
+        final int speedSteps = mPowerProfile.getNumSpeedSteps();
+        final double[] powerCpuNormal = new double[speedSteps];
+        final long[] cpuSpeedStepTimes = new long[speedSteps];
+        for (int p = 0; p < speedSteps; p++) {
+            powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
+        }
+        final double mobilePowerPerPacket = getMobilePowerPerPacket();
+        final double mobilePowerPerMs = getMobilePowerPerMs();
+        final double wifiPowerPerPacket = getWifiPowerPerPacket();
+        long totalAppWakelockTimeUs = 0;
+        BatterySipper osApp = null;
         mStatsPeriod = mTypeBatteryRealtime;
 
+        final ArrayList<BatterySipper> appList = new ArrayList<>();
+
+        // Max values used to normalize later.
+        double maxWifiPower = 0;
+        double maxCpuPower = 0;
+        double maxWakeLockPower = 0;
+        double maxMobileRadioPower = 0;
+        double maxGpsPower = 0;
+        double maxSensorPower = 0;
+
         final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
         final int NU = uidStats.size();
         for (int iu = 0; iu < NU; iu++) {
             final Uid u = uidStats.valueAt(iu);
-            final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
+            final BatterySipper app = new BatterySipper(
+                    BatterySipper.DrainType.APP, u, new double[]{0});
 
-            mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
-            mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
-            mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
-            mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
-            mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
-            mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
+            final Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
+            if (processStats.size() > 0) {
+                // Process CPU time.
 
-            final double totalPower = app.sumPower();
-            if (DEBUG && totalPower != 0) {
-                Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
-                        makemAh(totalPower)));
+                // Keep track of the package with highest drain.
+                double highestDrain = 0;
+
+                for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
+                        : processStats.entrySet()) {
+                    Uid.Proc ps = ent.getValue();
+                    app.cpuFgTime += ps.getForegroundTime(which);
+                    final long totalCpuTime = ps.getUserTime(which) + ps.getSystemTime(which);
+                    app.cpuTime += totalCpuTime;
+
+                    // Calculate the total CPU time spent at the various speed steps.
+                    long totalTimeAtSpeeds = 0;
+                    for (int step = 0; step < speedSteps; step++) {
+                        cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which);
+                        totalTimeAtSpeeds += cpuSpeedStepTimes[step];
+                    }
+                    totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
+
+                    // Then compute the ratio of time spent at each speed and figure out
+                    // the total power consumption.
+                    double cpuPower = 0;
+                    for (int step = 0; step < speedSteps; step++) {
+                        final double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds;
+                        final double cpuSpeedStepPower =
+                                ratio * totalCpuTime * powerCpuNormal[step];
+                        if (DEBUG && ratio != 0) {
+                            Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
+                                    + step + " ratio=" + makemAh(ratio) + " power="
+                                    + makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
+                        }
+                        cpuPower += cpuSpeedStepPower;
+                    }
+
+                    if (DEBUG && cpuPower != 0) {
+                        Log.d(TAG, String.format("process %s, cpu power=%s",
+                                ent.getKey(), makemAh(cpuPower / (60 * 60 * 1000))));
+                    }
+                    app.cpuPower += cpuPower;
+
+                    // Each App can have multiple packages and with multiple running processes.
+                    // Keep track of the package who's process has the highest drain.
+                    if (app.packageWithHighestDrain == null ||
+                            app.packageWithHighestDrain.startsWith("*")) {
+                        highestDrain = cpuPower;
+                        app.packageWithHighestDrain = ent.getKey();
+                    } else if (highestDrain < cpuPower && !ent.getKey().startsWith("*")) {
+                        highestDrain = cpuPower;
+                        app.packageWithHighestDrain = ent.getKey();
+                    }
+                }
+            }
+
+            // Ensure that the CPU times make sense.
+            if (app.cpuFgTime > app.cpuTime) {
+                if (DEBUG && app.cpuFgTime > app.cpuTime + 10000) {
+                    Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
+                }
+
+                // Statistics may not have been gathered yet.
+                app.cpuTime = app.cpuFgTime;
+            }
+
+            // Convert the CPU power to mAh
+            app.cpuPower /= (60 * 60 * 1000);
+            maxCpuPower = Math.max(maxCpuPower, app.cpuPower);
+
+            // Process wake lock usage
+            final Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
+                    u.getWakelockStats();
+            long wakeLockTimeUs = 0;
+            for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry
+                    : wakelockStats.entrySet()) {
+                final Uid.Wakelock wakelock = wakelockEntry.getValue();
+
+                // Only care about partial wake locks since full wake locks
+                // are canceled when the user turns the screen off.
+                BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+                if (timer != null) {
+                    wakeLockTimeUs += timer.getTotalTimeLocked(mRawRealtime, which);
+                }
+            }
+            app.wakeLockTime = wakeLockTimeUs / 1000; // convert to millis
+            totalAppWakelockTimeUs += wakeLockTimeUs;
+
+            // Add cost of holding a wake lock.
+            app.wakeLockPower = (app.wakeLockTime *
+                    mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60 * 60 * 1000);
+            if (DEBUG && app.wakeLockPower != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": wake "
+                        + app.wakeLockTime + " power=" + makemAh(app.wakeLockPower));
+            }
+            maxWakeLockPower = Math.max(maxWakeLockPower, app.wakeLockPower);
+
+            // Add cost of mobile traffic.
+            final long mobileActive = u.getMobileRadioActiveTime(mStatsType);
+            app.mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
+            app.mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
+            app.mobileActive = mobileActive / 1000;
+            app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);
+            app.mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, mStatsType);
+            app.mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, mStatsType);
+
+            if (mobileActive > 0) {
+                // We are tracking when the radio is up, so can use the active time to
+                // determine power use.
+                mAppMobileActive += mobileActive;
+                app.mobileRadioPower = (mobilePowerPerMs * mobileActive) / 1000;
+            } else {
+                // We are not tracking when the radio is up, so must approximate power use
+                // based on the number of packets.
+                app.mobileRadioPower = (app.mobileRxPackets + app.mobileTxPackets)
+                        * mobilePowerPerPacket;
+            }
+            if (DEBUG && app.mobileRadioPower != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+                        + (app.mobileRxPackets + app.mobileTxPackets)
+                        + " active time " + mobileActive
+                        + " power=" + makemAh(app.mobileRadioPower));
+            }
+            maxMobileRadioPower = Math.max(maxMobileRadioPower, app.mobileRadioPower);
+
+            // Add cost of wifi traffic
+            app.wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);
+            app.wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);
+            app.wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);
+            app.wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);
+
+            final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
+                    * wifiPowerPerPacket;
+            if (DEBUG && wifiPacketPower != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": wifi packets "
+                        + (app.wifiRxPackets + app.wifiTxPackets)
+                        + " power=" + makemAh(wifiPacketPower));
+            }
+
+            // Add cost of keeping WIFI running.
+            app.wifiRunningTime = u.getWifiRunningTime(mRawRealtime, which) / 1000;
+            mAppWifiRunning += app.wifiRunningTime;
+
+            final double wifiLockPower = (app.wifiRunningTime
+                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60 * 60 * 1000);
+            if (DEBUG && wifiLockPower != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": wifi running "
+                        + app.wifiRunningTime + " power=" + makemAh(wifiLockPower));
+            }
+
+            // Add cost of WIFI scans
+            final long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;
+            final double wifiScanPower = (wifiScanTimeMs
+                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN))
+                    /  (60 * 60 * 1000);
+            if (DEBUG && wifiScanPower != 0) {
+                Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs
+                        + " power=" + makemAh(wifiScanPower));
+            }
+
+            // Add cost of WIFI batch scans.
+            double wifiBatchScanPower = 0;
+            for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
+                final long batchScanTimeMs =
+                        u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;
+                final double batchScanPower = ((batchScanTimeMs
+                        * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))
+                ) / (60 * 60 * 1000);
+                if (DEBUG && batchScanPower != 0) {
+                    Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin
+                            + " time=" + batchScanTimeMs + " power=" + makemAh(batchScanPower));
+                }
+                wifiBatchScanPower += batchScanPower;
+            }
+
+            // Add up all the WiFi costs.
+            app.wifiPower = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
+            maxWifiPower = Math.max(maxWifiPower, app.wifiPower);
+
+            // Process Sensor usage
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
+            final int NSE = sensorStats.size();
+            for (int ise = 0; ise < NSE; ise++) {
+                final Uid.Sensor sensor = sensorStats.valueAt(ise);
+                final int sensorHandle = sensorStats.keyAt(ise);
+                final BatteryStats.Timer timer = sensor.getSensorTime();
+                final long sensorTime = timer.getTotalTimeLocked(mRawRealtime, which) / 1000;
+                double sensorPower = 0;
+                switch (sensorHandle) {
+                    case Uid.Sensor.GPS:
+                        app.gpsTime = sensorTime;
+                        app.gpsPower = (app.gpsTime
+                                * mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON))
+                                / (60 * 60 * 1000);
+                        sensorPower = app.gpsPower;
+                        maxGpsPower = Math.max(maxGpsPower, app.gpsPower);
+                        break;
+                    default:
+                        List<Sensor> sensorList = sensorManager.getSensorList(
+                                android.hardware.Sensor.TYPE_ALL);
+                        for (android.hardware.Sensor s : sensorList) {
+                            if (s.getHandle() == sensorHandle) {
+                                sensorPower = (sensorTime * s.getPower()) / (60 * 60 * 1000);
+                                app.sensorPower += sensorPower;
+                                break;
+                            }
+                        }
+                }
+                if (DEBUG && sensorPower != 0) {
+                    Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle
+                            + " time=" + sensorTime + " power=" + makemAh(sensorPower));
+                }
+            }
+            maxSensorPower = Math.max(maxSensorPower, app.sensorPower);
+
+            final double totalUnnormalizedPower = app.cpuPower + app.wifiPower + app.wakeLockPower
+                    + app.mobileRadioPower + app.gpsPower + app.sensorPower;
+            if (DEBUG && totalUnnormalizedPower != 0) {
+                Log.d(TAG, String.format("UID %d: total power=%s",
+                        u.getUid(), makemAh(totalUnnormalizedPower)));
             }
 
             // Add the app to the list if it is consuming power.
-            if (totalPower != 0 || u.getUid() == 0) {
-                //
-                // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
-                //
-                final int uid = app.getUid();
-                final int userId = UserHandle.getUserId(uid);
-                if (uid == Process.WIFI_UID) {
-                    mWifiSippers.add(app);
-                } else if (uid == Process.BLUETOOTH_UID) {
-                    mBluetoothSippers.add(app);
-                } else if (!forAllUsers && asUsers.get(userId) == null
-                        && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
-                    // We are told to just report this user's apps as one large entry.
-                    List<BatterySipper> list = mUserSippers.get(userId);
-                    if (list == null) {
-                        list = new ArrayList<>();
-                        mUserSippers.put(userId, list);
-                    }
-                    list.add(app);
-                } else {
-                    mUsageList.add(app);
-                }
+            if (totalUnnormalizedPower != 0 || u.getUid() == 0) {
+                appList.add(app);
+            }
+        }
 
-                if (uid == 0) {
-                    // The device has probably been awake for longer than the screen on
-                    // time and application wake lock time would account for.  Assign
-                    // this remainder to the OS, if possible.
-                    mWakelockPowerCalculator.calculateRemaining(app, mStats, mRawRealtime,
-                                                                mRawUptime, mStatsType);
-                    app.sumPower();
+        // Fetch real power consumption from hardware.
+        double actualTotalWifiPower = 0.0;
+        if (mStats.getWifiControllerActivity(BatteryStats.CONTROLLER_ENERGY, mStatsType) != 0) {
+            final double kDefaultVoltage = 3.36;
+            final long energy = mStats.getWifiControllerActivity(
+                    BatteryStats.CONTROLLER_ENERGY, mStatsType);
+            final double voltage = mPowerProfile.getAveragePowerOrDefault(
+                    PowerProfile.OPERATING_VOLTAGE_WIFI, kDefaultVoltage);
+            actualTotalWifiPower = energy / (voltage * 1000*60*60);
+        }
+
+        final int appCount = appList.size();
+        for (int i = 0; i < appCount; i++) {
+            // Normalize power where possible.
+            final BatterySipper app = appList.get(i);
+            if (actualTotalWifiPower != 0) {
+                app.wifiPower = (app.wifiPower / maxWifiPower) * actualTotalWifiPower;
+            }
+
+            // Assign the final power consumption here.
+            final double power = app.wifiPower + app.cpuPower + app.wakeLockPower
+                    + app.mobileRadioPower + app.gpsPower + app.sensorPower;
+            app.values[0] = app.value = power;
+
+            //
+            // Add the app to the app list, WiFi, Bluetooth, etc, or into "Other Users" list.
+            //
+
+            final int uid = app.getUid();
+            final int userId = UserHandle.getUserId(uid);
+            if (uid == Process.WIFI_UID) {
+                mWifiSippers.add(app);
+                mWifiPower += power;
+            } else if (uid == Process.BLUETOOTH_UID) {
+                mBluetoothSippers.add(app);
+                mBluetoothPower += power;
+            } else if (!forAllUsers && asUsers.get(userId) == null
+                    && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
+                // We are told to just report this user's apps as one large entry.
+                List<BatterySipper> list = mUserSippers.get(userId);
+                if (list == null) {
+                    list = new ArrayList<>();
+                    mUserSippers.put(userId, list);
                 }
+                list.add(app);
+
+                Double userPower = mUserPower.get(userId);
+                if (userPower == null) {
+                    userPower = power;
+                } else {
+                    userPower += power;
+                }
+                mUserPower.put(userId, userPower);
+            } else {
+                mUsageList.add(app);
+                if (power > mMaxPower) mMaxPower = power;
+                if (power > mMaxRealPower) mMaxRealPower = power;
+                mComputedPower += power;
+            }
+
+            if (uid == 0) {
+                osApp = app;
+            }
+        }
+
+        // The device has probably been awake for longer than the screen on
+        // time and application wake lock time would account for.  Assign
+        // this remainder to the OS, if possible.
+        if (osApp != null) {
+            long wakeTimeMillis = mBatteryUptime / 1000;
+            wakeTimeMillis -= (totalAppWakelockTimeUs / 1000)
+                    + (mStats.getScreenOnTime(mRawRealtime, which) / 1000);
+            if (wakeTimeMillis > 0) {
+                double power = (wakeTimeMillis
+                        * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE))
+                        /  (60*60*1000);
+                if (DEBUG) Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
+                        + makemAh(power));
+                osApp.wakeLockTime += wakeTimeMillis;
+                osApp.value += power;
+                osApp.values[0] += power;
+                if (osApp.value > mMaxPower) mMaxPower = osApp.value;
+                if (osApp.value > mMaxRealPower) mMaxRealPower = osApp.value;
+                mComputedPower += power;
             }
         }
     }
@@ -521,7 +744,7 @@
         double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
                 * phoneOnTimeMs / (60*60*1000);
         if (phoneOnPower != 0) {
-            addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
+            BatterySipper bs = addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
         }
     }
 
@@ -550,19 +773,54 @@
     }
 
     private void addRadioUsage() {
-        BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
-        mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime,
-                mStatsType);
-        radio.sumPower();
-        if (radio.totalPowerMah > 0) {
-            mUsageList.add(radio);
+        double power = 0;
+        final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+        long signalTimeMs = 0;
+        long noCoverageTimeMs = 0;
+        for (int i = 0; i < BINS; i++) {
+            long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, mRawRealtime, mStatsType)
+                    / 1000;
+            double p = (strengthTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ON, i))
+                        / (60*60*1000);
+            if (DEBUG && p != 0) {
+                Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+                        + makemAh(p));
+            }
+            power += p;
+            signalTimeMs += strengthTimeMs;
+            if (i == 0) {
+                noCoverageTimeMs = strengthTimeMs;
+            }
+        }
+        long scanningTimeMs = mStats.getPhoneSignalScanningTime(mRawRealtime, mStatsType)
+                / 1000;
+        double p = (scanningTimeMs * mPowerProfile.getAveragePower(
+                        PowerProfile.POWER_RADIO_SCANNING))
+                        / (60*60*1000);
+        if (DEBUG && p != 0) {
+            Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + makemAh(p));
+        }
+        power += p;
+        long radioActiveTimeUs = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType);
+        long remainingActiveTime = (radioActiveTimeUs - mAppMobileActive) / 1000;
+        if (remainingActiveTime > 0) {
+            power += getMobilePowerPerMs() * remainingActiveTime;
+        }
+        if (power != 0) {
+            BatterySipper bs =
+                    addEntry(BatterySipper.DrainType.CELL, signalTimeMs, power);
+            if (signalTimeMs != 0) {
+                bs.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
+            }
+            bs.mobileActive = remainingActiveTime;
+            bs.mobileActiveCount = mStats.getMobileRadioActiveUnknownCount(mStatsType);
         }
     }
 
     private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag) {
         for (int i=0; i<from.size(); i++) {
             BatterySipper wbs = from.get(i);
-            if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTimeMs);
+            if (DEBUG) Log.d(TAG, tag + " adding sipper " + wbs + ": cpu=" + wbs.cpuTime);
             bs.add(wbs);
         }
         bs.computeMobilemspp();
@@ -589,12 +847,41 @@
      * of WiFi to the WiFi subsystem.
      */
     private void addWiFiUsage() {
-        BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
-        mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
-        bs.sumPower();
-        if (bs.totalPowerMah > 0 || !mWifiSippers.isEmpty()) {
+        final long idleTimeMs = mStats.getWifiControllerActivity(
+                BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
+        final long txTimeMs = mStats.getWifiControllerActivity(
+                BatteryStats.CONTROLLER_TX_TIME, mStatsType);
+        final long rxTimeMs = mStats.getWifiControllerActivity(
+                BatteryStats.CONTROLLER_RX_TIME, mStatsType);
+        final long energy = mStats.getWifiControllerActivity(
+                BatteryStats.CONTROLLER_ENERGY, mStatsType);
+        final long totalTimeRunning = idleTimeMs + txTimeMs + rxTimeMs;
+
+        double powerDrain = 0;
+        if (energy == 0 && totalTimeRunning > 0) {
+            // Energy is not reported, which means we may have left over power drain not attributed
+            // to any app. Assign this power to the WiFi app.
+            // TODO(adamlesinski): This mimics the old behavior. However, mAppWifiRunningTime
+            // is the accumulation of the time each app kept the WiFi chip on. Multiple apps
+            // can do this at the same time, so these times do not add up to the total time
+            // the WiFi chip was on. Consider normalizing the time spent running and calculating
+            // power from that? Normalizing the times will assign a weight to each app which
+            // should better represent power usage.
+            powerDrain = ((totalTimeRunning - mAppWifiRunning)
+                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);
+        }
+
+        if (DEBUG && powerDrain != 0) {
+            Log.d(TAG, "Wifi active: time=" + (txTimeMs + rxTimeMs)
+                    + " power=" + makemAh(powerDrain));
+        }
+
+        // TODO(adamlesinski): mWifiPower is already added as a BatterySipper...
+        // Are we double counting here?
+        final double power = mWifiPower + powerDrain;
+        if (power > 0) {
+            BatterySipper bs = addEntry(BatterySipper.DrainType.WIFI, totalTimeRunning, power);
             aggregateSippers(bs, mWifiSippers, "WIFI");
-            mUsageList.add(bs);
         }
     }
 
@@ -603,10 +890,30 @@
      * Bluetooth Category.
      */
     private void addBluetoothUsage() {
-        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
-        mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime,
-                mStatsType);
-        if (bs.sumPower() > 0) {
+        final double kDefaultVoltage = 3.36;
+        final long idleTimeMs = mStats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_IDLE_TIME, mStatsType);
+        final long txTimeMs = mStats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_TX_TIME, mStatsType);
+        final long rxTimeMs = mStats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_RX_TIME, mStatsType);
+        final long energy = mStats.getBluetoothControllerActivity(
+                BatteryStats.CONTROLLER_ENERGY, mStatsType);
+        final double voltage = mPowerProfile.getAveragePowerOrDefault(
+                PowerProfile.OPERATING_VOLTAGE_BLUETOOTH, kDefaultVoltage);
+
+        // energy is measured in mA * V * ms, and we are interested in mAh
+        final double powerDrain = energy / (voltage * 60*60*1000);
+
+        if (DEBUG && powerDrain != 0) {
+            Log.d(TAG, "Bluetooth active: time=" + (txTimeMs + rxTimeMs)
+                    + " power=" + makemAh(powerDrain));
+        }
+
+        final long totalTime = idleTimeMs + txTimeMs + rxTimeMs;
+        final double power = mBluetoothPower + powerDrain;
+        if (power > 0) {
+            BatterySipper bs = addEntry(BatterySipper.DrainType.BLUETOOTH, totalTime, power);
             aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
         }
     }
@@ -621,16 +928,55 @@
     }
 
     private void addUserUsage() {
-        for (int i = 0; i < mUserSippers.size(); i++) {
+        for (int i=0; i<mUserSippers.size(); i++) {
             final int userId = mUserSippers.keyAt(i);
-            BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
+            final List<BatterySipper> sippers = mUserSippers.valueAt(i);
+            Double userPower = mUserPower.get(userId);
+            double power = (userPower != null) ? userPower : 0.0;
+            BatterySipper bs = addEntry(BatterySipper.DrainType.USER, 0, power);
             bs.userId = userId;
-            aggregateSippers(bs, mUserSippers.valueAt(i), "User");
-            bs.sumPower();
-            mUsageList.add(bs);
+            aggregateSippers(bs, sippers, "User");
         }
     }
 
+    /**
+     * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
+     */
+    private double getMobilePowerPerPacket() {
+        final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
+        final double MOBILE_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)
+                / 3600;
+
+        final long mobileRx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, mStatsType);
+        final long mobileTx = mStats.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, mStatsType);
+        final long mobileData = mobileRx + mobileTx;
+
+        final long radioDataUptimeMs
+                = mStats.getMobileRadioActiveTime(mRawRealtime, mStatsType) / 1000;
+        final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
+                ? (mobileData / (double)radioDataUptimeMs)
+                : (((double)MOBILE_BPS) / 8 / 2048);
+
+        return (MOBILE_POWER / mobilePps) / (60*60);
+    }
+
+    /**
+     * Return estimated power (in mAs) of keeping the radio up
+     */
+    private double getMobilePowerPerMs() {
+        return mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) / (60*60*1000);
+    }
+
+    /**
+     * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
+     */
+    private double getWifiPowerPerPacket() {
+        final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
+        final double WIFI_POWER = mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
+                / 3600;
+        return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
+    }
+
     private void processMiscUsage() {
         addUserUsage();
         addPhoneUsage();
@@ -646,10 +992,15 @@
     }
 
     private BatterySipper addEntry(DrainType drainType, long time, double power) {
-        BatterySipper bs = new BatterySipper(drainType, null, 0);
-        bs.usagePowerMah = power;
-        bs.usageTimeMs = time;
-        bs.sumPower();
+        mComputedPower += power;
+        if (power > mMaxRealPower) mMaxRealPower = power;
+        return addEntryNoTotal(drainType, time, power);
+    }
+
+    private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {
+        if (power > mMaxPower) mMaxPower = power;
+        BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});
+        bs.usageTime = time;
         mUsageList.add(bs);
         return bs;
     }
@@ -664,7 +1015,7 @@
 
     public long getStatsPeriod() { return mStatsPeriod; }
 
-    public int getStatsType() { return mStatsType; }
+    public int getStatsType() { return mStatsType; };
 
     public double getMaxPower() { return mMaxPower; }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 793d0d3..05ed3ab 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -55,7 +55,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.view.Display;
@@ -96,7 +95,6 @@
 public final class BatteryStatsImpl extends BatteryStats {
     private static final String TAG = "BatteryStatsImpl";
     private static final boolean DEBUG = false;
-    private static final boolean DEBUG_ENERGY = false;
     private static final boolean DEBUG_HISTORY = false;
     private static final boolean USE_OLD_HISTORY = false;   // for debugging.
 
@@ -184,20 +182,22 @@
     // elapsed time by the number of active timers to arrive at that timer's share of the time.
     // In order to do this, we must refresh each timer whenever the number of active timers
     // changes.
-    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
-    final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers = new SparseArray<>();
-    final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<>();
-    final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
-    final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
-    final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<StopwatchTimer>();
+    final SparseArray<ArrayList<StopwatchTimer>> mSensorTimers
+            = new SparseArray<ArrayList<StopwatchTimer>>();
+    final ArrayList<StopwatchTimer> mWifiRunningTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mFullWifiLockTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mWifiMulticastTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
+    final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
+            new SparseArray<ArrayList<StopwatchTimer>>();
+    final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
+    final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
 
     // Last partial timers we use for distributing CPU usage.
-    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
 
     // These are the objects that will want to do something when the device
     // is unplugged from power.
@@ -227,7 +227,7 @@
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
     final HistoryItem mHistoryAddTmp = new HistoryItem();
-    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>();
+    final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
     int mReadHistoryChars;
@@ -450,8 +450,6 @@
 
     private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
 
-    private PowerProfile mPowerProfile;
-
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
@@ -930,12 +928,6 @@
         long mUnpluggedTime;
 
         /**
-         * The total time this timer has been running until the latest mark has been set.
-         * Subtract this from mTotalTime to get the time spent running since the mark was set.
-         */
-        long mTimeBeforeMark;
-
-        /**
          * Constructs from a parcel.
          * @param type
          * @param timeBase
@@ -953,7 +945,6 @@
             mLoadedTime = in.readLong();
             mLastTime = 0;
             mUnpluggedTime = in.readLong();
-            mTimeBeforeMark = in.readLong();
             timeBase.add(this);
             if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTime);
         }
@@ -973,7 +964,7 @@
          * so can be completely dropped.
          */
         boolean reset(boolean detachIfReset) {
-            mTotalTime = mLoadedTime = mLastTime = mTimeBeforeMark = 0;
+            mTotalTime = mLoadedTime = mLastTime = 0;
             mCount = mLoadedCount = mLastCount = 0;
             if (detachIfReset) {
                 detach();
@@ -994,10 +985,8 @@
             out.writeLong(computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs)));
             out.writeLong(mLoadedTime);
             out.writeLong(mUnpluggedTime);
-            out.writeLong(mTimeBeforeMark);
         }
 
-        @Override
         public void onTimeStarted(long elapsedRealtime, long timeBaseUptime, long baseRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "unplug #" + mType + ": realtime=" + baseRealtime
@@ -1013,7 +1002,6 @@
             }
         }
 
-        @Override
         public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
             if (DEBUG && mType < 0) {
                 Log.v(TAG, "plug #" + mType + ": realtime=" + baseRealtime
@@ -1067,13 +1055,6 @@
             return val;
         }
 
-        @Override
-        public long getTimeSinceMarkLocked(long elapsedRealtimeUs) {
-            long val = computeRunTimeLocked(mTimeBase.getRealtime(elapsedRealtimeUs));
-            return val - mTimeBeforeMark;
-        }
-
-        @Override
         public void logState(Printer pw, String prefix) {
             pw.println(prefix + "mCount=" + mCount
                     + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
@@ -1099,9 +1080,6 @@
             mCount = mLoadedCount = in.readInt();
             mLastCount = 0;
             mUnpluggedCount = mCount;
-
-            // When reading the summary, we set the mark to be the latest information.
-            mTimeBeforeMark = mTotalTime;
         }
     }
 
@@ -1497,6 +1475,21 @@
             return mNesting > 0;
         }
 
+        long checkpointRunningLocked(long elapsedRealtimeMs) {
+            if (mNesting > 0) {
+                // We are running...
+                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
+                if (mTimerPool != null) {
+                    return refreshTimersLocked(batteryRealtime, mTimerPool, this);
+                }
+                final long heldTime = batteryRealtime - mUpdateTime;
+                mUpdateTime = batteryRealtime;
+                mTotalTime += heldTime;
+                return heldTime;
+            }
+            return 0;
+        }
+
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -1574,7 +1567,6 @@
             return mCount;
         }
 
-        @Override
         boolean reset(boolean detachIfReset) {
             boolean canDetach = mNesting <= 0;
             super.reset(canDetach && detachIfReset);
@@ -1585,7 +1577,6 @@
             return canDetach;
         }
 
-        @Override
         void detach() {
             super.detach();
             if (mTimerPool != null) {
@@ -1593,31 +1584,10 @@
             }
         }
 
-        @Override
         void readSummaryFromParcelLocked(Parcel in) {
             super.readSummaryFromParcelLocked(in);
             mNesting = 0;
         }
-
-        /**
-         * Set the mark so that we can query later for the total time the timer has
-         * accumulated since this point. The timer can be running or not.
-         *
-         * @param elapsedRealtimeMs the current elapsed realtime in milliseconds.
-         */
-        public void setMark(long elapsedRealtimeMs) {
-            final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
-            if (mNesting > 0) {
-                // We are running.
-                if (mTimerPool != null) {
-                    refreshTimersLocked(batteryRealtime, mTimerPool, this);
-                } else {
-                    mTotalTime += batteryRealtime - mUpdateTime;
-                    mUpdateTime = batteryRealtime;
-                }
-            }
-            mTimeBeforeMark = mTotalTime;
-        }
     }
 
     public abstract class OverflowArrayMap<T> {
@@ -3920,6 +3890,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3935,6 +3906,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
     }
@@ -3992,6 +3964,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -4007,6 +3980,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
     }
@@ -4114,8 +4088,7 @@
         // During device boot, qtaguid isn't enabled until after the inital
         // loading of battery stats. Now that they're enabled, take our initial
         // snapshot for future delta calculation.
-        final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
-        updateMobileRadioStateLocked(elapsedRealtimeMs);
+        updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
         updateWifiStateLocked(null);
     }
 
@@ -4396,18 +4369,6 @@
         LongSamplingCounter mMobileRadioActiveCount;
 
         /**
-         * The amount of time this uid has kept the WiFi controller in idle, tx, and rx mode.
-         */
-        LongSamplingCounter[] mWifiControllerTime =
-                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
-
-        /**
-         * The amount of time this uid has kept the Bluetooth controller in idle, tx, and rx mode.
-         */
-        LongSamplingCounter[] mBluetoothControllerTime =
-                new LongSamplingCounter[NUM_CONTROLLER_ACTIVITY_TYPES];
-
-        /**
          * The CPU times we had at the last history details update.
          */
         long mLastStepUserTime;
@@ -4443,22 +4404,22 @@
         /**
          * The statistics we have collected for this uid's sensor activations.
          */
-        final SparseArray<Sensor> mSensorStats = new SparseArray<>();
+        final SparseArray<Sensor> mSensorStats = new SparseArray<Sensor>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final ArrayMap<String, Proc> mProcessStats = new ArrayMap<>();
+        final ArrayMap<String, Proc> mProcessStats = new ArrayMap<String, Proc>();
 
         /**
          * The statistics we have collected for this uid's processes.
          */
-        final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<>();
+        final ArrayMap<String, Pkg> mPackageStats = new ArrayMap<String, Pkg>();
 
         /**
          * The transient wake stats we have collected for this uid's pids.
          */
-        final SparseArray<Pid> mPids = new SparseArray<>();
+        final SparseArray<Pid> mPids = new SparseArray<Pid>();
 
         public Uid(int uid) {
             mUid = uid;
@@ -4619,13 +4580,6 @@
             }
         }
 
-        public void noteWifiControllerActivityLocked(int type, long timeMs) {
-            if (mWifiControllerTime[type] == null) {
-                mWifiControllerTime[type] = new LongSamplingCounter(mOnBatteryTimeBase);
-            }
-            mWifiControllerTime[type].addCountLocked(timeMs);
-        }
-
         public StopwatchTimer createAudioTurnedOnTimerLocked() {
             if (mAudioTurnedOnTimer == null) {
                 mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
@@ -4941,15 +4895,6 @@
                     ? (int)mMobileRadioActiveCount.getCountLocked(which) : 0;
         }
 
-        @Override
-        public long getWifiControllerActivity(int type, int which) {
-            if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES &&
-                    mWifiControllerTime[type] != null) {
-                return mWifiControllerTime[type].getCountLocked(which);
-            }
-            return 0;
-        }
-
         void initNetworkActivityLocked() {
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
             mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -5033,16 +4978,6 @@
                 mMobileRadioActiveCount.reset(false);
             }
 
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mWifiControllerTime[i] != null) {
-                    mWifiControllerTime[i].reset(false);
-                }
-
-                if (mBluetoothControllerTime[i] != null) {
-                    mBluetoothControllerTime[i].reset(false);
-                }
-            }
-
             final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap();
             for (int iw=wakeStats.size()-1; iw>=0; iw--) {
                 Wakelock wl = wakeStats.valueAt(iw);
@@ -5165,16 +5100,6 @@
                         mNetworkPacketActivityCounters[i].detach();
                     }
                 }
-
-                for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                    if (mWifiControllerTime[i] != null) {
-                        mWifiControllerTime[i].detach();
-                    }
-
-                    if (mBluetoothControllerTime[i] != null) {
-                        mBluetoothControllerTime[i].detach();
-                    }
-                }
                 mPids.clear();
             }
 
@@ -5264,7 +5189,6 @@
             } else {
                 out.writeInt(0);
             }
-
             if (mAudioTurnedOnTimer != null) {
                 out.writeInt(1);
                 mAudioTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -5316,24 +5240,6 @@
             } else {
                 out.writeInt(0);
             }
-
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mWifiControllerTime[i] != null) {
-                    out.writeInt(1);
-                    mWifiControllerTime[i].writeToParcel(out);
-                } else {
-                    out.writeInt(0);
-                }
-            }
-
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (mBluetoothControllerTime[i] != null) {
-                    out.writeInt(1);
-                    mBluetoothControllerTime[i].writeToParcel(out);
-                } else {
-                    out.writeInt(0);
-                }
-            }
         }
 
         void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
@@ -5483,22 +5389,6 @@
                 mNetworkByteActivityCounters = null;
                 mNetworkPacketActivityCounters = null;
             }
-
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (in.readInt() != 0) {
-                    mWifiControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
-                } else {
-                    mWifiControllerTime[i] = null;
-                }
-            }
-
-            for (int i = 0; i < NUM_CONTROLLER_ACTIVITY_TYPES; i++) {
-                if (in.readInt() != 0) {
-                    mBluetoothControllerTime[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
-                } else {
-                    mBluetoothControllerTime[i] = null;
-                }
-            }
         }
 
         /**
@@ -6754,12 +6644,6 @@
         readFromParcel(p);
     }
 
-    public void setPowerProfile(PowerProfile profile) {
-        synchronized (this) {
-            mPowerProfile = profile;
-        }
-    }
-
     public void setCallback(BatteryCallback cb) {
         mCallback = cb;
     }
@@ -7483,12 +7367,9 @@
      * @param info The energy information from the WiFi controller.
      */
     public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
-        final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
-        NetworkStats delta = null;
+        final NetworkStats delta;
         try {
-            if (!ArrayUtils.isEmpty(mWifiIfaces)) {
-                delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
-            }
+            delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
         } catch (IOException e) {
             Slog.wtf(TAG, "Failed to get wifi network stats", e);
             return;
@@ -7498,19 +7379,14 @@
             return;
         }
 
-        SparseLongArray rxPackets = new SparseLongArray();
-        SparseLongArray txPackets = new SparseLongArray();
-        long totalTxPackets = 0;
-        long totalRxPackets = 0;
         if (delta != null) {
             final int size = delta.size();
             for (int i = 0; i < size; i++) {
                 final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
 
-                if (DEBUG_ENERGY) {
+                if (DEBUG) {
                     Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                            + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
-                            + " txPackets=" + entry.txPackets);
+                            + " tx=" + entry.txBytes);
                 }
 
                 if (entry.rxBytes == 0 || entry.txBytes == 0) {
@@ -7522,13 +7398,6 @@
                         entry.rxPackets);
                 u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
                         entry.txPackets);
-                rxPackets.put(u.getUid(), entry.rxPackets);
-                txPackets.put(u.getUid(), entry.txPackets);
-
-                // Sum the total number of packets so that the Rx Power and Tx Power can
-                // be evenly distributed amongst the apps.
-                totalRxPackets += entry.rxPackets;
-                totalTxPackets += entry.txPackets;
 
                 mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
                         entry.rxBytes);
@@ -7542,119 +7411,6 @@
         }
 
         if (info != null) {
-            // Measured in mAms
-            final long txTimeMs = info.getControllerTxTimeMillis();
-            final long rxTimeMs = info.getControllerRxTimeMillis();
-            final long idleTimeMs = info.getControllerIdleTimeMillis();
-            final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
-
-            long leftOverRxTimeMs = rxTimeMs;
-
-            if (DEBUG_ENERGY) {
-                Slog.d(TAG, "------ BEGIN WiFi power blaming ------");
-                Slog.d(TAG, "  Tx Time:    " + txTimeMs + " ms");
-                Slog.d(TAG, "  Rx Time:    " + rxTimeMs + " ms");
-                Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
-                Slog.d(TAG, "  Total Time: " + totalTimeMs + " ms");
-            }
-
-            long totalWifiLockTimeMs = 0;
-            long totalScanTimeMs = 0;
-
-            // On the first pass, collect some totals so that we can normalize power
-            // calculations if we need to.
-            final int uidStatsSize = mUidStats.size();
-            for (int i = 0; i < uidStatsSize; i++) {
-                final Uid uid = mUidStats.valueAt(i);
-
-                // Sum the total scan power for all apps.
-                totalScanTimeMs += uid.mWifiScanTimer.getTimeSinceMarkLocked(
-                        elapsedRealtimeMs * 1000) / 1000;
-
-                // Sum the total time holding wifi lock for all apps.
-                totalWifiLockTimeMs += uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
-                        elapsedRealtimeMs * 1000) / 1000;
-            }
-
-            if (DEBUG_ENERGY && totalScanTimeMs > rxTimeMs) {
-                Slog.d(TAG, "  !Estimated scan time > Actual rx time (" + totalScanTimeMs + " ms > "
-                        + rxTimeMs + " ms). Normalizing scan time.");
-            }
-
-            // Actually assign and distribute power usage to apps.
-            for (int i = 0; i < uidStatsSize; i++) {
-                final Uid uid = mUidStats.valueAt(i);
-
-                long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked(
-                        elapsedRealtimeMs * 1000) / 1000;
-                if (scanTimeSinceMarkMs > 0) {
-                    // Set the new mark so that next time we get new data since this point.
-                    uid.mWifiScanTimer.setMark(elapsedRealtimeMs);
-
-                    if (totalScanTimeMs > rxTimeMs) {
-                        // Our total scan time is more than the reported Rx time.
-                        // This is possible because the cost of a scan is approximate.
-                        // Let's normalize the result so that we evenly blame each app
-                        // scanning.
-                        //
-                        // This means that we may have apps that received packets not be blamed
-                        // for this, but this is fine as scans are relatively more expensive.
-                        scanTimeSinceMarkMs = (rxTimeMs * scanTimeSinceMarkMs) / totalScanTimeMs;
-                    }
-
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  ScanTime for UID " + uid.getUid() + ": "
-                                + scanTimeSinceMarkMs + " ms)");
-                    }
-                    uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, scanTimeSinceMarkMs);
-                    leftOverRxTimeMs -= scanTimeSinceMarkMs;
-                }
-
-                // Distribute evenly the power consumed while Idle to each app holding a WiFi
-                // lock.
-                final long wifiLockTimeSinceMarkMs = uid.mFullWifiLockTimer.getTimeSinceMarkLocked(
-                        elapsedRealtimeMs * 1000) / 1000;
-                if (wifiLockTimeSinceMarkMs > 0) {
-                    // Set the new mark so that next time we get new data since this point.
-                    uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs);
-
-                    final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs)
-                            / totalWifiLockTimeMs;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  IdleTime for UID " + uid.getUid() + ": "
-                                + myIdleTimeMs + " ms");
-                    }
-                    uid.noteWifiControllerActivityLocked(CONTROLLER_IDLE_TIME, myIdleTimeMs);
-                }
-            }
-
-            if (DEBUG_ENERGY) {
-                Slog.d(TAG, "  New RxPower: " + leftOverRxTimeMs + " ms");
-            }
-
-            // Distribute the Tx power appropriately between all apps that transmitted packets.
-            for (int i = 0; i < txPackets.size(); i++) {
-                final Uid uid = getUidStatsLocked(txPackets.keyAt(i));
-                final long myTxTimeMs = (txPackets.valueAt(i) * txTimeMs) / totalTxPackets;
-                if (DEBUG_ENERGY) {
-                    Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
-                }
-                uid.noteWifiControllerActivityLocked(CONTROLLER_TX_TIME, myTxTimeMs);
-            }
-
-            // Distribute the remaining Rx power appropriately between all apps that received
-            // packets.
-            for (int i = 0; i < rxPackets.size(); i++) {
-                final Uid uid = getUidStatsLocked(rxPackets.keyAt(i));
-                final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs) / totalRxPackets;
-                if (DEBUG_ENERGY) {
-                    Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
-                }
-                uid.noteWifiControllerActivityLocked(CONTROLLER_RX_TIME, myRxTimeMs);
-            }
-
-            // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
-
             // Update WiFi controller stats.
             mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
                     info.getControllerRxTimeMillis());
@@ -7662,29 +7418,19 @@
                     info.getControllerTxTimeMillis());
             mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
-
-            final double powerDrainMaMs;
-            if (mPowerProfile.getAveragePower(
-                    PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) == 0) {
-                powerDrainMaMs = 0.0;
-            } else {
-                powerDrainMaMs = info.getControllerEnergyUsed()
-                        / mPowerProfile.getAveragePower(
-                        PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE);
-            }
-            mWifiActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked((long) powerDrainMaMs);
+            mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+                    info.getControllerEnergyUsed());
         }
     }
 
     /**
      * Distribute Cell radio energy info and network traffic to apps.
      */
-    public void updateMobileRadioStateLocked(final long elapsedRealtimeMs) {
-        NetworkStats delta = null;
+    public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
+        final NetworkStats delta;
+
         try {
-            if (!ArrayUtils.isEmpty(mMobileIfaces)) {
-                delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
-            }
+            delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
         } catch (IOException e) {
             Slog.wtf(TAG, "Failed to get mobile network stats", e);
             return;
@@ -7694,24 +7440,14 @@
             return;
         }
 
-        long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
-                elapsedRealtimeMs * 1000);
-        mMobileRadioActivePerAppTimer.setMark(elapsedRealtimeMs);
+        long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
         long totalPackets = delta.getTotalPackets();
 
         final int size = delta.size();
         for (int i = 0; i < size; i++) {
             final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
 
-            if (entry.rxBytes == 0 || entry.txBytes == 0) {
-                continue;
-            }
-
-            if (DEBUG_ENERGY) {
-                Slog.d(TAG, "Mobile uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                        + " tx=" + entry.txBytes + " rxPackets=" + entry.rxPackets
-                        + " txPackets=" + entry.txPackets);
-            }
+            if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
 
             final Uid u = getUidStatsLocked(mapUid(entry.uid));
             u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
@@ -7752,14 +7488,14 @@
      * @param info The energy information from the bluetooth controller.
      */
     public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
-        if (info != null && mOnBatteryInternal && false) {
+        if (info != null && mOnBatteryInternal) {
             mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
                     info.getControllerRxTimeMillis());
             mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
                     info.getControllerTxTimeMillis());
             mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
                     info.getControllerIdleTimeMillis());
-            mBluetoothActivityCounters[CONTROLLER_POWER_DRAIN].addCountLocked(
+            mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
                     info.getControllerEnergyUsed());
         }
     }
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
deleted file mode 100644
index 3557209..0000000
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.util.Log;
-
-public class BluetoothPowerCalculator extends PowerCalculator {
-    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private static final String TAG = "BluetoothPowerCalculator";
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        // No per-app distribution yet.
-    }
-
-    @Override
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        final long idleTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_IDLE_TIME, statsType);
-        final long txTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_TX_TIME, statsType);
-        final long rxTimeMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_RX_TIME, statsType);
-        final long powerMaMs = stats.getBluetoothControllerActivity(
-                BatteryStats.CONTROLLER_POWER_DRAIN, statsType);
-        final double powerMah = powerMaMs / (double)(1000*60*60);
-        final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
-
-        if (DEBUG && powerMah != 0) {
-            Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
-                    + " power=" + BatteryStatsHelper.makemAh(powerMah));
-        }
-
-        app.usagePowerMah = powerMah;
-        app.usageTimeMs = totalTimeMs;
-    }
-}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
deleted file mode 100644
index 6c3f958..0000000
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.util.ArrayMap;
-import android.util.Log;
-
-public class CpuPowerCalculator extends PowerCalculator {
-    private static final String TAG = "CpuPowerCalculator";
-    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-
-    private final double[] mPowerCpuNormal;
-
-    /**
-     * Reusable array for calculations.
-     */
-    private final long[] mSpeedStepTimes;
-
-    public CpuPowerCalculator(PowerProfile profile) {
-        final int speedSteps = profile.getNumSpeedSteps();
-        mPowerCpuNormal = new double[speedSteps];
-        mSpeedStepTimes = new long[speedSteps];
-        for (int p = 0; p < speedSteps; p++) {
-            mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
-        }
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        final int speedSteps = mSpeedStepTimes.length;
-
-        // Keep track of the package with highest drain.
-        double highestDrain = 0;
-
-        final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
-        final int processStatsCount = processStats.size();
-        for (int i = 0; i < processStatsCount; i++) {
-            final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
-            final String processName = processStats.keyAt(i);
-
-            app.cpuFgTimeMs += ps.getForegroundTime(statsType);
-            final long totalCpuTime = ps.getUserTime(statsType) + ps.getSystemTime(statsType);
-            app.cpuTimeMs += totalCpuTime;
-
-            // Calculate the total CPU time spent at the various speed steps.
-            long totalTimeAtSpeeds = 0;
-            for (int step = 0; step < speedSteps; step++) {
-                mSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, statsType);
-                totalTimeAtSpeeds += mSpeedStepTimes[step];
-            }
-            totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1);
-
-            // Then compute the ratio of time spent at each speed and figure out
-            // the total power consumption.
-            double cpuPower = 0;
-            for (int step = 0; step < speedSteps; step++) {
-                final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;
-                final double cpuSpeedStepPower = ratio * totalCpuTime * mPowerCpuNormal[step];
-                if (DEBUG && ratio != 0) {
-                    Log.d(TAG, "UID " + u.getUid() + ": CPU step #"
-                            + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power="
-                            + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000)));
-                }
-                cpuPower += cpuSpeedStepPower;
-            }
-
-            if (DEBUG && cpuPower != 0) {
-                Log.d(TAG, String.format("process %s, cpu power=%s",
-                        processName, BatteryStatsHelper.makemAh(cpuPower / (60 * 60 * 1000))));
-            }
-            app.cpuPowerMah += cpuPower;
-
-            // Each App can have multiple packages and with multiple running processes.
-            // Keep track of the package who's process has the highest drain.
-            if (app.packageWithHighestDrain == null ||
-                    app.packageWithHighestDrain.startsWith("*")) {
-                highestDrain = cpuPower;
-                app.packageWithHighestDrain = processName;
-            } else if (highestDrain < cpuPower && !processName.startsWith("*")) {
-                highestDrain = cpuPower;
-                app.packageWithHighestDrain = processName;
-            }
-        }
-
-        // Ensure that the CPU times make sense.
-        if (app.cpuFgTimeMs > app.cpuTimeMs) {
-            if (DEBUG && app.cpuFgTimeMs > app.cpuTimeMs + 10000) {
-                Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");
-            }
-
-            // Statistics may not have been gathered yet.
-            app.cpuTimeMs = app.cpuFgTimeMs;
-        }
-
-        // Convert the CPU power to mAh
-        app.cpuPowerMah /= (60 * 60 * 1000);
-    }
-}
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
deleted file mode 100644
index 9711c3b..0000000
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.telephony.SignalStrength;
-import android.util.Log;
-
-public class MobileRadioPowerCalculator extends PowerCalculator {
-    private static final String TAG = "MobileRadioPowerController";
-    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private final double mPowerRadioOn;
-    private final double[] mPowerBins = new double[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
-    private final double mPowerScan;
-    private BatteryStats mStats;
-    private long mTotalAppMobileActiveMs = 0;
-
-    /**
-     * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
-     */
-    private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
-        final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
-        final double MOBILE_POWER = mPowerRadioOn / 3600;
-
-        final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
-                statsType);
-        final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
-                statsType);
-        final long mobileData = mobileRx + mobileTx;
-
-        final long radioDataUptimeMs =
-                mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
-        final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
-                ? (mobileData / (double)radioDataUptimeMs)
-                : (((double)MOBILE_BPS) / 8 / 2048);
-        return (MOBILE_POWER / mobilePps) / (60*60);
-    }
-
-    public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {
-        mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);
-        for (int i = 0; i < mPowerBins.length; i++) {
-            mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE, i);
-        }
-        mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);
-        mStats = stats;
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        // Add cost of mobile traffic.
-        app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
-                statsType);
-        app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
-                statsType);
-        app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
-        app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
-        app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
-                statsType);
-        app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
-                statsType);
-
-        if (app.mobileActive > 0) {
-            // We are tracking when the radio is up, so can use the active time to
-            // determine power use.
-            mTotalAppMobileActiveMs += app.mobileActive;
-            app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);
-        } else {
-            // We are not tracking when the radio is up, so must approximate power use
-            // based on the number of packets.
-            app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
-                    * getMobilePowerPerPacket(rawRealtimeUs, statsType);
-        }
-        if (DEBUG && app.mobileRadioPowerMah != 0) {
-            Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
-                    + (app.mobileRxPackets + app.mobileTxPackets)
-                    + " active time " + app.mobileActive
-                    + " power=" + BatteryStatsHelper.makemAh(app.mobileRadioPowerMah));
-        }
-    }
-
-    @Override
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        double power = 0;
-        long signalTimeMs = 0;
-        long noCoverageTimeMs = 0;
-        for (int i = 0; i < mPowerBins.length; i++) {
-            long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
-                    / 1000;
-            final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);
-            if (DEBUG && p != 0) {
-                Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
-                        + BatteryStatsHelper.makemAh(p));
-            }
-            power += p;
-            signalTimeMs += strengthTimeMs;
-            if (i == 0) {
-                noCoverageTimeMs = strengthTimeMs;
-            }
-        }
-
-        final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
-                / 1000;
-        final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);
-        if (DEBUG && p != 0) {
-            Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs
-                    + " power=" + BatteryStatsHelper.makemAh(p));
-        }
-        power += p;
-        long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
-        long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
-        if (remainingActiveTimeMs > 0) {
-            power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);
-        }
-
-        if (power != 0) {
-            app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
-            app.mobileActive = remainingActiveTimeMs;
-            app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
-            app.mobileRadioPowerMah = power;
-        }
-    }
-
-    @Override
-    public void reset() {
-        mTotalAppMobileActiveMs = 0;
-    }
-
-    public void reset(BatteryStats stats) {
-        reset();
-        mStats = stats;
-    }
-}
diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java
deleted file mode 100644
index cd69d68..0000000
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-
-/**
- * Calculates power use of a device subsystem for an app.
- */
-public abstract class PowerCalculator {
-    /**
-     * Calculate the amount of power an app used for this subsystem.
-     * @param app The BatterySipper that represents the power use of an app.
-     * @param u The recorded stats for the app.
-     * @param rawRealtimeUs The raw system realtime in microseconds.
-     * @param rawUptimeUs The raw system uptime in microseconds.
-     * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
-     *                  {@link BatteryStats#STATS_SINCE_CHARGED}, or
-     *                  {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
-     */
-    public abstract void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                                      long rawUptimeUs, int statsType);
-
-    /**
-     * Calculate the remaining power that can not be attributed to an app.
-     * @param app The BatterySipper that will represent this remaining power.
-     * @param stats The BatteryStats object from which to retrieve data.
-     * @param rawRealtimeUs The raw system realtime in microseconds.
-     * @param rawUptimeUs The raw system uptime in microseconds.
-     * @param statsType The type of stats. Can be {@link BatteryStats#STATS_CURRENT},
-     *                  {@link BatteryStats#STATS_SINCE_CHARGED}, or
-     *                  {@link BatteryStats#STATS_SINCE_UNPLUGGED}.
-     */
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-    }
-
-    /**
-     * Reset any state maintained in this calculator.
-     */
-    public void reset() {
-    }
-}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 7e6706c..944eb5a 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -18,7 +18,6 @@
 
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 
 import com.android.internal.util.XmlUtils;
@@ -76,21 +75,10 @@
      */
     public static final String POWER_WIFI_ACTIVE = "wifi.active";
 
-    //
-    // Updated power constants. These are not estimated, they are real world
-    // currents and voltages for the underlying bluetooth and wifi controllers.
-    //
-
-    public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
-    public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
-    public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
-    public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
-
-    public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
-    public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
-    public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
-    public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
-            "bluetooth.controller.voltage";
+    /**
+     * Operating voltage of the WiFi controller.
+     */
+    public static final String OPERATING_VOLTAGE_WIFI = "wifi.voltage";
 
     /**
      * Power consumption when GPS is on.
@@ -112,6 +100,10 @@
      */
     public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
 
+    /**
+     * Operating voltage of the Bluetooth controller.
+     */
+    public static final String OPERATING_VOLTAGE_BLUETOOTH = "bluetooth.voltage";
 
     /**
      * Power consumption when screen is on, not including the backlight power.
@@ -170,7 +162,7 @@
      */
     public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
 
-    static final HashMap<String, Object> sPowerMap = new HashMap<>();
+    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
 
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
@@ -188,8 +180,7 @@
 
     private void readPowerValuesFromXml(Context context) {
         int id = com.android.internal.R.xml.power_profile;
-        final Resources resources = context.getResources();
-        XmlResourceParser parser = resources.getXml(id);
+        XmlResourceParser parser = context.getResources().getXml(id);
         boolean parsingArray = false;
         ArrayList<Double> array = new ArrayList<Double>();
         String arrayName = null;
@@ -240,36 +231,6 @@
         } finally {
             parser.close();
         }
-
-        // Now collect other config variables.
-        int[] configResIds = new int[] {
-                com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
-                com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
-                com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
-                com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
-                com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
-                com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
-                com.android.internal.R.integer.config_wifi_tx_cur_ma,
-                com.android.internal.R.integer.config_wifi_operating_voltage_mv,
-        };
-
-        String[] configResIdKeys = new String[] {
-                POWER_BLUETOOTH_CONTROLLER_IDLE,
-                POWER_BLUETOOTH_CONTROLLER_RX,
-                POWER_BLUETOOTH_CONTROLLER_TX,
-                POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
-                POWER_WIFI_CONTROLLER_IDLE,
-                POWER_WIFI_CONTROLLER_RX,
-                POWER_WIFI_CONTROLLER_TX,
-                POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
-        };
-
-        for (int i = 0; i < configResIds.length; i++) {
-            int value = resources.getInteger(configResIds[i]);
-            if (value > 0) {
-                sPowerMap.put(configResIdKeys[i], (double) value);
-            }
-        }
     }
 
     /**
diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java
deleted file mode 100644
index c98639b..0000000
--- a/core/java/com/android/internal/os/SensorPowerCalculator.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.os.BatteryStats;
-import android.util.SparseArray;
-
-import java.util.List;
-
-public class SensorPowerCalculator extends PowerCalculator {
-    private final List<Sensor> mSensors;
-    private final double mGpsPowerOn;
-
-    public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
-        mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
-        mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        // Process Sensor usage
-        final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
-        final int NSE = sensorStats.size();
-        for (int ise = 0; ise < NSE; ise++) {
-            final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
-            final int sensorHandle = sensorStats.keyAt(ise);
-            final BatteryStats.Timer timer = sensor.getSensorTime();
-            final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
-            switch (sensorHandle) {
-                case BatteryStats.Uid.Sensor.GPS:
-                    app.gpsTimeMs = sensorTime;
-                    app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);
-                    break;
-                default:
-                    final int sensorsCount = mSensors.size();
-                    for (int i = 0; i < sensorsCount; i++) {
-                        final Sensor s = mSensors.get(i);
-                        if (s.getHandle() == sensorHandle) {
-                            app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);
-                            break;
-                        }
-                    }
-                    break;
-            }
-        }
-    }
-}
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
deleted file mode 100644
index 7575010f..0000000
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-import android.util.ArrayMap;
-import android.util.Log;
-
-public class WakelockPowerCalculator extends PowerCalculator {
-    private static final String TAG = "WakelockPowerCalculator";
-    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
-    private final double mPowerWakelock;
-    private long mTotalAppWakelockTimeMs = 0;
-
-    public WakelockPowerCalculator(PowerProfile profile) {
-        mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawUptimeUs,
-                             long rawRealtimeUs, int statsType) {
-        long wakeLockTimeUs = 0;
-        final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
-                u.getWakelockStats();
-        final int wakelockStatsCount = wakelockStats.size();
-        for (int i = 0; i < wakelockStatsCount; i++) {
-            final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
-
-            // Only care about partial wake locks since full wake locks
-            // are canceled when the user turns the screen off.
-            BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
-            if (timer != null) {
-                wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
-            }
-        }
-        app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
-        mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
-
-        // Add cost of holding a wake lock.
-        app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
-        if (DEBUG && app.wakeLockPowerMah != 0) {
-            Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
-                    + " power=" + BatteryStatsHelper.makemAh(app.wakeLockPowerMah));
-        }
-    }
-
-    @Override
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;
-        wakeTimeMillis -= mTotalAppWakelockTimeMs
-                + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
-        if (wakeTimeMillis > 0) {
-            final double power = (wakeTimeMillis * mPowerWakelock) / (1000*60*60);
-            if (DEBUG) {
-                Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power "
-                        + BatteryStatsHelper.makemAh(power));
-            }
-            app.wakeLockTimeMs += wakeTimeMillis;
-            app.wakeLockPowerMah += power;
-        }
-    }
-
-    @Override
-    public void reset() {
-        mTotalAppWakelockTimeMs = 0;
-    }
-}
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
deleted file mode 100644
index 4e77f6b..0000000
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-
-/**
- * WiFi power calculator for when BatteryStats supports energy reporting
- * from the WiFi controller.
- */
-public class WifiPowerCalculator extends PowerCalculator {
-    private final double mIdleCurrentMa;
-    private final double mTxCurrentMa;
-    private final double mRxCurrentMa;
-    private double mTotalAppPowerDrain = 0;
-
-    public WifiPowerCalculator(PowerProfile profile) {
-        mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
-        mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
-        mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
-                statsType);
-        final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
-        final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
-        app.wifiRunningTimeMs = idleTime + rxTime + txTime;
-        app.wifiPowerMah =
-                ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa))
-                / (1000*60*60);
-        mTotalAppPowerDrain += app.wifiPowerMah;
-
-        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-    }
-
-    @Override
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME,
-                statsType);
-        final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME,
-                statsType);
-        final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME,
-                statsType);
-        app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
-
-        double powerDrain = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN,
-                statsType) / (1000*60*60);
-        if (powerDrain == 0) {
-            // Some controllers do not report power drain, so we can calculate it here.
-            powerDrain = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa)
-                    + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
-        }
-        app.wifiPowerMah = Math.max(0, powerDrain - mTotalAppPowerDrain);
-    }
-
-    @Override
-    public void reset() {
-        mTotalAppPowerDrain = 0;
-    }
-}
diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java
deleted file mode 100644
index 0172367..0000000
--- a/core/java/com/android/internal/os/WifiPowerEstimator.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.os.BatteryStats;
-
-/**
- * Estimates WiFi power usage based on timers in BatteryStats.
- */
-public class WifiPowerEstimator extends PowerCalculator {
-    private final double mWifiPowerPerPacket;
-    private final double mWifiPowerOn;
-    private final double mWifiPowerScan;
-    private final double mWifiPowerBatchScan;
-    private long mTotalAppWifiRunningTimeMs = 0;
-
-    public WifiPowerEstimator(PowerProfile profile) {
-        mWifiPowerPerPacket = getWifiPowerPerPacket(profile);
-        mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON);
-        mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN);
-        mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN);
-    }
-
-    /**
-     * Return estimated power (in mAs) of sending a byte with the Wi-Fi radio.
-     */
-    private static double getWifiPowerPerPacket(PowerProfile profile) {
-        final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system
-        final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
-                / 3600;
-        return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
-    }
-
-    @Override
-    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-        app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-        app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
-                statsType);
-        app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
-                statsType);
-
-        final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
-                * mWifiPowerPerPacket;
-
-        app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
-        mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
-        final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
-
-        final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType);
-        final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
-
-        double wifiBatchScanPower = 0;
-        for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
-            final long batchScanTimeMs =
-                    u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
-            final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
-            wifiBatchScanPower += batchScanPower;
-        }
-
-        app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
-    }
-
-    @Override
-    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
-                                   long rawUptimeUs, int statsType) {
-        final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType)
-                / 1000;
-        final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn)
-                / (1000*60*60);
-        app.wifiRunningTimeMs = totalRunningTimeMs;
-        app.wifiPowerMah = Math.max(0, powerDrain);
-    }
-
-    @Override
-    public void reset() {
-        mTotalAppWifiRunningTimeMs = 0;
-    }
-}
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index c153904..7d56e9e 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -17,10 +17,13 @@
 package com.android.internal.util;
 
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 
 /**
  * Utility class for image analysis and processing.
@@ -117,4 +120,40 @@
                 && Math.abs(r - b) < TOLERANCE
                 && Math.abs(g - b) < TOLERANCE;
     }
+
+    /**
+     * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+     */
+    public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+            int maxHeight) {
+        if (drawable == null) {
+            return null;
+        }
+        int originalWidth = drawable.getIntrinsicWidth();
+        int originalHeight = drawable.getIntrinsicHeight();
+
+        if ((originalWidth <= maxWidth) && (originalHeight <= maxHeight) &&
+                (drawable instanceof BitmapDrawable)) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+        if (originalHeight <= 0 || originalWidth <= 0) {
+            return null;
+        }
+
+        // create a new bitmap, scaling down to fit the max dimensions of
+        // a large notification icon if necessary
+        float ratio = Math.min((float) maxWidth / (float) originalWidth,
+                (float) maxHeight / (float) originalHeight);
+        ratio = Math.min(1.0f, ratio);
+        int scaledWidth = (int) (ratio * originalWidth);
+        int scaledHeight = (int) (ratio * originalHeight);
+        Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
+
+        // and paint our app bitmap on it
+        Canvas canvas = new Canvas(result);
+        drawable.setBounds(0, 0, scaledWidth, scaledHeight);
+        drawable.draw(canvas);
+
+        return result;
+    }
 }
diff --git a/core/res/res/drawable/ic_check_circle_24px.xml b/core/res/res/drawable/ic_check_circle_24px.xml
new file mode 100644
index 0000000..066a8a7
--- /dev/null
+++ b/core/res/res/drawable/ic_check_circle_24px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zm-2.0,15.0l-5.0,-5.0 1.41,-1.41L10.0,14.17l7.59,-7.59L19.0,8.0l-9.0,9.0z"/>
+</vector>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7e963954..aaf252a 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -93,5 +93,10 @@
   <item type="id" name="undo" />
   <item type="id" name="redo" />
   <item type="id" name="replaceText" />
-  <item type="id" name="accessibility_action_show_on_screen" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SHOW_ON_SCREEN}. -->
+  <item type="id" name="accessibilityActionShowOnScreen" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_TO_POSITION}. -->
+  <item type="id" name="accessibilityActionScrollToPosition" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f1707d2..24d17a4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2646,11 +2646,11 @@
   <public type="id" name="undo" />
   <public type="id" name="redo" />
   <public type="id" name="replaceText" />
+  <public type="id" name="accessibilityActionShowOnScreen" />
+  <public type="id" name="accessibilityActionScrollToPosition" />
 
   <public type="attr" name="allowUndo" />
-
   <public type="attr" name="colorBackgroundFloating" />
-
   <public type="attr" name="extractNativeLibs" />
   <public type="attr" name="usesCleartextTraffic" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4d90932..7dc3ff7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5162,6 +5162,11 @@
     <!-- Lock-to-app unlock password string -->
     <string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
 
+    <!-- Notification shown when device owner silently installs a package -->
+    <string name="package_installed_device_owner">Installed by your administrator</string>
+    <!-- Notification shown when device owner silently deletes a package -->
+    <string name="package_deleted_device_owner">Deleted by your administrator</string>
+
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
     <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 220d5e7..b4ba316 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -694,6 +694,8 @@
   <java-symbol type="string" name="lock_to_app_unlock_pin" />
   <java-symbol type="string" name="lock_to_app_unlock_pattern" />
   <java-symbol type="string" name="lock_to_app_unlock_password" />
+  <java-symbol type="string" name="package_installed_device_owner" />
+  <java-symbol type="string" name="package_deleted_device_owner" />
   <java-symbol type="string" name="lockscreen_access_pattern_cell_added" />
   <java-symbol type="string" name="lockscreen_access_pattern_cleared" />
   <java-symbol type="string" name="lockscreen_access_pattern_detected" />
@@ -1172,6 +1174,7 @@
   <java-symbol type="drawable" name="ic_audio_vol" />
   <java-symbol type="drawable" name="ic_audio_vol_mute" />
   <java-symbol type="drawable" name="ic_bullet_key_permission" />
+  <java-symbol type="drawable" name="ic_check_circle_24px" />
   <java-symbol type="drawable" name="ic_contact_picture" />
   <java-symbol type="drawable" name="ic_dialog_usb" />
   <java-symbol type="drawable" name="ic_emergency" />
@@ -2207,8 +2210,6 @@
   <java-symbol type="string" name="storage_sd_card" />
   <java-symbol type="string" name="storage_usb" />
 
-  <java-symbol type="id" name="accessibility_action_show_on_screen" />
-
   <!-- Floating toolbar -->
   <java-symbol type="layout" name="floating_popup_container" />
   <java-symbol type="layout" name="floating_popup_menu_button" />
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 0bd1dbd..ed690de 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -466,74 +466,65 @@
             throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
         }
 
-        if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) {
-            throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString
-                    + ", parameter spec: "
-                    + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm()));
-        }
-
         KeymasterArguments args = new KeymasterArguments();
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
                 KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
 
-        if (digest != null) {
-            // Digest available from JCA key algorithm
-            if (params.getDigest() != null) {
-                // Digest also specified in parameters -- check that these two match
-                if (digest != params.getDigest()) {
-                    throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString
+        @KeyStoreKeyConstraints.DigestEnum int digests;
+        if (params.isDigestsSpecified()) {
+            // Digest(s) specified in parameters
+            if (digest != null) {
+                // Digest also specified in the JCA key algorithm name.
+                if ((params.getDigests() & digest) != digest) {
+                    throw new KeyStoreException("Key digest mismatch"
+                            + ". Key: " + keyAlgorithmString
                             + ", parameter spec: "
-                            + KeyStoreKeyConstraints.Digest.toString(params.getDigest()));
+                            + KeyStoreKeyConstraints.Digest.allToString(params.getDigests()));
                 }
             }
+            digests = params.getDigests();
         } else {
-            // Digest not available from JCA key algorithm
-            digest = params.getDigest();
+            // No digest specified in parameters
+            if (digest != null) {
+                // Digest specified in the JCA key algorithm name.
+                digests = digest;
+            } else {
+                digests = 0;
+            }
         }
-        if (digest != null) {
-            args.addInt(KeymasterDefs.KM_TAG_DIGEST,
-                    KeyStoreKeyConstraints.Digest.toKeymaster(digest));
+        for (int keymasterDigest : KeyStoreKeyConstraints.Digest.allToKeymaster(digests)) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST, keymasterDigest);
+        }
+        if (digests != 0) {
+            // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+            // This code will blow up if mode than one digest is specified.
             Integer digestOutputSizeBytes =
                     KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
             if (digestOutputSizeBytes != null) {
-                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
                 // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
                 args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
             }
         }
         if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
-            if (digest == null) {
-                throw new IllegalStateException("Digest algorithm must be specified for key"
-                        + " algorithm " + keyAlgorithmString);
+            if (digests == 0) {
+                throw new KeyStoreException("At least one digest algorithm must be specified"
+                        + " for key algorithm " + keyAlgorithmString);
             }
         }
 
-        @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
-                ? params.getPurposes()
-                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
-                        | KeyStoreKeyConstraints.Purpose.DECRYPT
-                        | KeyStoreKeyConstraints.Purpose.SIGN
-                        | KeyStoreKeyConstraints.Purpose.VERIFY);
-        for (int keymasterPurpose :
-            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+        int purposes = params.getPurposes();
+        for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
             args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
         }
-        if (params.getBlockMode() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
-                    KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode()));
+        for (int keymasterBlockMode :
+            KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
         }
-        if (params.getPadding() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_PADDING,
-                    KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding()));
+        for (int keymasterPadding :
+            KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
         }
-        if (params.getMaxUsesPerBoot() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot());
-        }
-        if (params.getMinSecondsBetweenOperations() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
-                    params.getMinSecondsBetweenOperations());
-        }
-        if (params.getUserAuthenticators().isEmpty()) {
+        if (params.getUserAuthenticators() == 0) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
             args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
@@ -544,7 +535,7 @@
             // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
             // that.
         }
-        if (params.getUserAuthenticationValidityDurationSeconds() != null) {
+        if (params.getUserAuthenticationValidityDurationSeconds() != -1) {
             args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     params.getUserAuthenticationValidityDurationSeconds());
         }
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 7058383..0e490cd 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -19,12 +19,8 @@
 import android.content.Context;
 import android.text.TextUtils;
 
-import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
 
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
@@ -33,13 +29,13 @@
  * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
  * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
  *
- * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
- * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
- * some UI to ask the user to unlock or initialize the Android KeyStore facility.
+ * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API using the
+ * {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up some UI to
+ * ask the user to unlock or initialize the Android KeyStore facility.
  *
  * <p>After generation, the {@code keyStoreAlias} is used with the
  * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
- * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
+ * interface to retrieve the {@link SecretKey}.
  *
  * @hide
  */
@@ -52,13 +48,11 @@
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
-    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
-    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
-    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
-    private final Integer mMinSecondsBetweenOperations;
-    private final Integer mMaxUsesPerBoot;
-    private final Set<Integer> mUserAuthenticators;
-    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+    private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mInvalidatedOnNewFingerprintEnrolled;
 
     private KeyGeneratorSpec(
@@ -69,20 +63,18 @@
             Date keyValidityStart,
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
-            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
-            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
-            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
-            Integer minSecondsBetweenOperations,
-            Integer maxUsesPerBoot,
-            Set<Integer> userAuthenticators,
-            Integer userAuthenticationValidityDurationSeconds,
+            @KeyStoreKeyConstraints.PurposeEnum int purposes,
+            @KeyStoreKeyConstraints.PaddingEnum int paddings,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+            int userAuthenticationValidityDurationSeconds,
             boolean invalidatedOnNewFingerprintEnrolled) {
         if (context == null) {
             throw new IllegalArgumentException("context == null");
         } else if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
-        } else if ((userAuthenticationValidityDurationSeconds != null)
-                && (userAuthenticationValidityDurationSeconds < 0)) {
+        } else if ((userAuthenticationValidityDurationSeconds < 0)
+                && (userAuthenticationValidityDurationSeconds != -1)) {
             throw new IllegalArgumentException(
                     "userAuthenticationValidityDurationSeconds must not be negative");
         }
@@ -95,13 +87,9 @@
         mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
         mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
         mPurposes = purposes;
-        mPadding = padding;
-        mBlockMode = blockMode;
-        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
-        mMaxUsesPerBoot = maxUsesPerBoot;
-        mUserAuthenticators = (userAuthenticators != null)
-                ? new HashSet<Integer>(userAuthenticators)
-                : Collections.<Integer>emptySet();
+        mPaddings = paddings;
+        mBlockModes = blockModes;
+        mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
     }
@@ -148,8 +136,6 @@
      * Gets the time instant after which the key is no longer valid for decryption and verification.
      *
      * @return instant or {@code null} if not restricted.
-     *
-     * @hide
      */
     public Date getKeyValidityForConsumptionEnd() {
         return mKeyValidityForConsumptionEnd;
@@ -166,78 +152,43 @@
 
     /**
      * Gets the set of purposes for which the key can be used.
-     *
-     * @return set of purposes or {@code null} if the key can be used for any purpose.
      */
-    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
         return mPurposes;
     }
 
     /**
-     * Gets the padding scheme to which the key is restricted.
-     *
-     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     * Gets the set of padding schemes to which the key is restricted.
      */
-    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
-        return mPadding;
+    public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+        return mPaddings;
     }
 
     /**
-     * Gets the block mode to which the key is restricted when used for encryption or decryption.
-     *
-     * @return block more or {@code null} if block mode is not restricted.
-     *
-     * @hide
+     * Gets the set of block modes to which the key is restricted.
      */
-    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
-        return mBlockMode;
+    public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+        return mBlockModes;
     }
 
     /**
-     * Gets the minimum number of seconds that must expire since the most recent use of the key
-     * before it can be used again.
+     * Gets the set of user authenticators which protect access to this key. The key can only be
+     * used iff the user has authenticated to at least one of these user authenticators.
      *
-     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
-     *         can be used.
-     *
-     * @hide
+     * @return user authenticators or {@code 0} if the key can be used without user authentication.
      */
-    public Integer getMinSecondsBetweenOperations() {
-        return mMinSecondsBetweenOperations;
-    }
-
-    /**
-     * Gets the number of times the key can be used without rebooting the device.
-     *
-     * @return maximum number of times or {@code null} if there is no restriction.
-     * @hide
-     */
-    public Integer getMaxUsesPerBoot() {
-        return mMaxUsesPerBoot;
-    }
-
-    /**
-     * Gets the user authenticators which protect access to this key. The key can only be used iff
-     * the user has authenticated to at least one of these user authenticators.
-     *
-     * @return user authenticators or empty set if the key can be used without user authentication.
-     *
-     * @hide
-     */
-    public Set<Integer> getUserAuthenticators() {
-        return new HashSet<Integer>(mUserAuthenticators);
+    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+        return mUserAuthenticators;
     }
 
     /**
      * Gets the duration of time (seconds) for which this key can be used after the user
      * successfully authenticates to one of the associated user authenticators.
      *
-     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
      *         is required for every use of the key.
-     *
-     * @hide
      */
-    public Integer getUserAuthenticationValidityDurationSeconds() {
+    public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
     }
 
@@ -269,13 +220,11 @@
         private Date mKeyValidityStart;
         private Date mKeyValidityForOriginationEnd;
         private Date mKeyValidityForConsumptionEnd;
-        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
-        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
-        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
-        private Integer mMinSecondsBetweenOperations;
-        private Integer mMaxUsesPerBoot;
-        private Set<Integer> mUserAuthenticators;
-        private Integer mUserAuthenticationValidityDurationSeconds;
+        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+        private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mInvalidatedOnNewFingerprintEnrolled;
 
         /**
@@ -335,8 +284,6 @@
          * <b>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityEnd(Date)
-         *
-         * @hide
          */
         public Builder setKeyValidityStart(Date startDate) {
             mKeyValidityStart = startDate;
@@ -351,8 +298,6 @@
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
-         *
-         * @hide
          */
         public Builder setKeyValidityEnd(Date endDate) {
             setKeyValidityForOriginationEnd(endDate);
@@ -366,8 +311,6 @@
          * <b>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
-         *
-         * @hide
          */
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
             mKeyValidityForOriginationEnd = endDate;
@@ -381,8 +324,6 @@
          * <b>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForOriginationEnd(Date)
-         *
-         * @hide
          */
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
             mKeyValidityForConsumptionEnd = endDate;
@@ -390,11 +331,9 @@
         }
 
         /**
-         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         * Restricts the key to being used only for the provided set of purposes.
          *
-         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
-         *
-         * @hide
+         * <p>This restriction must be specified. There is no default.
          */
         public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
             mPurposes = purposes;
@@ -402,53 +341,24 @@
         }
 
         /**
-         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * Restricts the key to being used only with the provided padding schemes. Attempts to use
          * the key with any other padding will be rejected.
          *
          * <p>This restriction must be specified for keys which are used for encryption/decryption.
-         *
-         * @hide
          */
-        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
-            mPadding = padding;
+        public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+            mPaddings = paddings;
             return this;
         }
 
         /**
-         * Restricts the key to being used only with the provided block mode when encrypting or
-         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         * Restricts the key to being used only with the provided block modes. Attempts to use the
+         * key with any other block modes will be rejected.
          *
          * <p>This restriction must be specified for keys which are used for encryption/decryption.
-         *
-         * @hide
          */
-        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
-            mBlockMode = blockMode;
-            return this;
-        }
-
-        /**
-         * Sets the minimum number of seconds that must expire since the most recent use of the key
-         * before it can be used again.
-         *
-         * <p>By default, there is no restriction on how frequently a key can be used.
-         *
-         * @hide
-         */
-        public Builder setMinSecondsBetweenOperations(int seconds) {
-            mMinSecondsBetweenOperations = seconds;
-            return this;
-        }
-
-        /**
-         * Sets the maximum number of times a key can be used without rebooting the device.
-         *
-         * <p>By default, the key can be used for an unlimited number of times.
-         *
-         * @hide
-         */
-        public Builder setMaxUsesPerBoot(int count) {
-            mMaxUsesPerBoot = count;
+        public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+            mBlockModes = blockModes;
             return this;
         }
 
@@ -462,12 +372,10 @@
          *        without user authentication.
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
-         *
-         * @hide
          */
-        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
-            mUserAuthenticators =
-                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+        public Builder setUserAuthenticators(
+                @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+            mUserAuthenticators = userAuthenticators;
             return this;
         }
 
@@ -480,9 +388,7 @@
          * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
          *        every use of the key.
          *
-         * @see #setUserAuthenticators(Set)
-         *
-         * @hide
+         * @see #setUserAuthenticators(int)
          */
         public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
             mUserAuthenticationValidityDurationSeconds = seconds;
@@ -511,10 +417,18 @@
          * @throws IllegalArgumentException if a required field is missing or violates a constraint.
          */
         public KeyGeneratorSpec build() {
-            return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
-                    mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
-                    mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
-                    mUserAuthenticators, mUserAuthenticationValidityDurationSeconds,
+            return new KeyGeneratorSpec(mContext,
+                    mKeystoreAlias,
+                    mFlags,
+                    mKeySize,
+                    mKeyValidityStart,
+                    mKeyValidityForOriginationEnd,
+                    mKeyValidityForConsumptionEnd,
+                    mPurposes,
+                    mPaddings,
+                    mBlockModes,
+                    mUserAuthenticators,
+                    mUserAuthenticationValidityDurationSeconds,
                     mInvalidatedOnNewFingerprintEnrolled);
         }
     }
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index dd62e9a..52b7097 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -24,10 +24,7 @@
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -81,21 +78,17 @@
 
     private final Date mKeyValidityForConsumptionEnd;
 
-    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
 
-    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.DigestEnum int mDigests;
 
-    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
 
-    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
 
-    private final Integer mMinSecondsBetweenOperations;
+    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
 
-    private final Integer mMaxUsesPerBoot;
-
-    private final Set<Integer> mUserAuthenticators;
-
-    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final int mUserAuthenticationValidityDurationSeconds;
 
     private final boolean mInvalidatedOnNewFingerprintEnrolled;
 
@@ -137,14 +130,12 @@
             Date keyValidityStart,
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
-            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
-            @KeyStoreKeyConstraints.DigestEnum Integer digest,
-            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
-            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
-            Integer minSecondsBetweenOperations,
-            Integer maxUsesPerBoot,
-            Set<Integer> userAuthenticators,
-            Integer userAuthenticationValidityDurationSeconds,
+            @KeyStoreKeyConstraints.PurposeEnum int purposes,
+            @KeyStoreKeyConstraints.DigestEnum int digests,
+            @KeyStoreKeyConstraints.PaddingEnum int paddings,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+            int userAuthenticationValidityDurationSeconds,
             boolean invalidatedOnNewFingerprintEnrolled) {
         if (context == null) {
             throw new IllegalArgumentException("context == null");
@@ -160,8 +151,8 @@
             throw new IllegalArgumentException("endDate == null");
         } else if (endDate.before(startDate)) {
             throw new IllegalArgumentException("endDate < startDate");
-        } else if ((userAuthenticationValidityDurationSeconds != null)
-                && (userAuthenticationValidityDurationSeconds < 0)) {
+        } else if ((userAuthenticationValidityDurationSeconds < 0)
+                && (userAuthenticationValidityDurationSeconds != -1)) {
             throw new IllegalArgumentException(
                     "userAuthenticationValidityDurationSeconds must not be negative");
         }
@@ -180,14 +171,10 @@
         mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
         mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
         mPurposes = purposes;
-        mDigest = digest;
-        mPadding = padding;
-        mBlockMode = blockMode;
-        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
-        mMaxUsesPerBoot = maxUsesPerBoot;
-        mUserAuthenticators = (userAuthenticators != null)
-                ? new HashSet<Integer>(userAuthenticators)
-                : Collections.<Integer>emptySet();
+        mDigests = digests;
+        mPaddings = paddings;
+        mBlockModes = blockModes;
+        mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
     }
@@ -200,8 +187,7 @@
             AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
             Date startDate, Date endDate, int flags) {
         this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
-                endDate, flags, startDate, endDate, endDate, null, null, null, null, null, null,
-                null, null, false);
+                endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1, false);
     }
 
     /**
@@ -327,90 +313,52 @@
     /**
      * Gets the set of purposes for which the key can be used.
      *
-     * @return set of purposes or {@code null} if the key can be used for any purpose.
-     *
      * @hide
      */
-    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
         return mPurposes;
     }
 
     /**
-     * Gets the digest to which the key is restricted.
-     *
-     * @return digest or {@code null} if the digest is not restricted.
+     * Gets the set of digests to which the key is restricted.
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
-        return mDigest;
+    public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+        return mDigests;
     }
 
     /**
-     * Gets the padding scheme to which the key is restricted.
-     *
-     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     * Gets the set of padding schemes to which the key is restricted.
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
-        return mPadding;
+    public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+        return mPaddings;
     }
 
     /**
-     * Gets the block mode to which the key is restricted when used for encryption or decryption.
-     *
-     * @return block more or {@code null} if block mode is not restricted.
+     * Gets the set of block modes to which the key is restricted.
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
-        return mBlockMode;
+    public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+        return mBlockModes;
     }
 
     /**
-     * Gets the minimum number of seconds that must expire since the most recent use of the private
-     * key before it can be used again.
+     * Gets the set of user authenticators which protect access to the private key. The key can only
+     * be used iff the user has authenticated to at least one of these user authenticators.
      *
      * <p>This restriction applies only to private key operations. Public key operations are not
      * restricted.
      *
-     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
-     *         can be used.
+     * @return user authenticators or {@code 0} if the key can be used without user authentication.
      *
      * @hide
      */
-    public Integer getMinSecondsBetweenOperations() {
-        return mMinSecondsBetweenOperations;
-    }
-
-    /**
-     * Gets the number of times the private key can be used without rebooting the device.
-     *
-     * <p>This restriction applies only to private key operations. Public key operations are not
-     * restricted.
-     *
-     * @return maximum number of times or {@code null} if there is no restriction.
-     *
-     * @hide
-     */
-    public Integer getMaxUsesPerBoot() {
-        return mMaxUsesPerBoot;
-    }
-
-    /**
-     * Gets the user authenticators which protect access to the private key. The key can only be
-     * used iff the user has authenticated to at least one of these user authenticators.
-     *
-     * <p>This restriction applies only to private key operations. Public key operations are not
-     * restricted.
-     *
-     * @return user authenticators or empty set if the key can be used without user authentication.
-     *
-     * @hide
-     */
-    public Set<Integer> getUserAuthenticators() {
-        return new HashSet<Integer>(mUserAuthenticators);
+    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+        return mUserAuthenticators;
     }
 
     /**
@@ -420,12 +368,12 @@
      * <p>This restriction applies only to private key operations. Public key operations are not
      * restricted.
      *
-     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
      *         is required for every use of the key.
      *
      * @hide
      */
-    public Integer getUserAuthenticationValidityDurationSeconds() {
+    public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
     }
 
@@ -490,21 +438,17 @@
 
         private Date mKeyValidityForConsumptionEnd;
 
-        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
 
-        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+        private @KeyStoreKeyConstraints.DigestEnum int mDigests;
 
-        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
 
-        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
 
-        private Integer mMinSecondsBetweenOperations;
+        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
 
-        private Integer mMaxUsesPerBoot;
-
-        private Set<Integer> mUserAuthenticators;
-
-        private Integer mUserAuthenticationValidityDurationSeconds;
+        private int mUserAuthenticationValidityDurationSeconds = -1;
 
         private boolean mInvalidatedOnNewFingerprintEnrolled;
 
@@ -694,9 +638,9 @@
         }
 
         /**
-         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         * Restricts the key to being used only for the provided set of purposes.
          *
-         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         * <p>This restriction must be specified. There is no default.
          *
          * @hide
          */
@@ -706,28 +650,28 @@
         }
 
         /**
-         * Restricts the key to being used only with the provided digest. Attempts to use the key
+         * Restricts the key to being used only with the provided digests. Attempts to use the key
          * with any other digests be rejected.
          *
          * <p>This restriction must be specified for keys which are used for signing/verification.
          *
          * @hide
          */
-        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
-            mDigest = digest;
+        public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) {
+            mDigests = digests;
             return this;
         }
 
         /**
-         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * Restricts the key to being used only with the provided padding schemes. Attempts to use
          * the key with any other padding will be rejected.
          *
          * <p>This restriction must be specified for keys which are used for encryption/decryption.
          *
          * @hide
          */
-        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
-            mPadding = padding;
+        public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+            mPaddings = paddings;
             return this;
         }
 
@@ -739,39 +683,8 @@
          *
          * @hide
          */
-        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
-            mBlockMode = blockMode;
-            return this;
-        }
-
-        /**
-         * Sets the minimum number of seconds that must expire since the most recent use of the key
-         * before it can be used again.
-         *
-         * <p>By default, there is no restriction on how frequently a key can be used.
-         *
-         * <p>This restriction applies only to private key operations. Public key operations are not
-         * restricted.
-         *
-         * @hide
-         */
-        public Builder setMinSecondsBetweenOperations(int seconds) {
-            mMinSecondsBetweenOperations = seconds;
-            return this;
-        }
-
-        /**
-         * Sets the maximum number of times a key can be used without rebooting the device.
-         *
-         * <p>By default, the key can be used for an unlimited number of times.
-         *
-         * <p>This restriction applies only to private key operations. Public key operations are not
-         * restricted.
-         *
-         * @hide
-         */
-        public Builder setMaxUsesPerBoot(int count) {
-            mMaxUsesPerBoot = count;
+        public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+            mBlockModes = blockModes;
             return this;
         }
 
@@ -784,16 +697,16 @@
          * <p>This restriction applies only to private key operations. Public key operations are not
          * restricted.
          *
-         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed
          *        without user authentication.
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          *
          * @hide
          */
-        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
-            mUserAuthenticators =
-                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+        public Builder setUserAuthenticators(
+                @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+            mUserAuthenticators = userAuthenticators;
             return this;
         }
 
@@ -809,7 +722,7 @@
          * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
          *        every use of the key.
          *
-         * @see #setUserAuthenticators(Set)
+         * @see #setUserAuthenticators(int)
          *
          * @hide
          */
@@ -855,11 +768,9 @@
                     mKeyValidityForOriginationEnd,
                     mKeyValidityForConsumptionEnd,
                     mPurposes,
-                    mDigest,
-                    mPadding,
-                    mBlockMode,
-                    mMinSecondsBetweenOperations,
-                    mMaxUsesPerBoot,
+                    mDigests,
+                    mPaddings,
+                    mBlockModes,
                     mUserAuthenticators,
                     mUserAuthenticationValidityDurationSeconds,
                     mInvalidatedOnNewFingerprintEnrolled);
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index 75034d1..7137a9a 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -21,12 +21,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.Locale;
-import java.util.Set;
 
 /**
  * Constraints for {@code AndroidKeyStore} keys.
@@ -37,7 +33,8 @@
     private KeyStoreKeyConstraints() {}
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
+    @IntDef(flag = true,
+            value = {Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
     public @interface PurposeEnum {}
 
     /**
@@ -67,11 +64,6 @@
         public static final int VERIFY = 1 << 3;
 
         /**
-         * Number of flags defined above. Needs to be kept in sync with the flags above.
-         */
-        private static final int VALUE_COUNT = 4;
-
-        /**
          * @hide
          */
         public static int toKeymaster(@PurposeEnum int purpose) {
@@ -110,22 +102,12 @@
         /**
          * @hide
          */
-        public static int[] allToKeymaster(int purposes) {
-            int[] result = new int[VALUE_COUNT];
-            int resultCount = 0;
-            int purpose = 1;
-            for (int i = 0; i < 32; i++) {
-                if ((purposes & 1) != 0) {
-                    result[resultCount] = toKeymaster(purpose);
-                    resultCount++;
-                }
-                purposes >>>= 1;
-                purpose <<= 1;
-                if (purposes == 0) {
-                    break;
-                }
+        public static int[] allToKeymaster(@PurposeEnum int purposes) {
+            int[] result = getSetFlags(purposes);
+            for (int i = 0; i < result.length; i++) {
+                result[i] = toKeymaster(result[i]);
             }
-            return Arrays.copyOf(result, resultCount);
+            return result;
         }
 
         /**
@@ -244,7 +226,8 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7})
+    @IntDef(flag = true,
+            value = {Padding.NONE, Padding.PKCS7})
     public @interface PaddingEnum {}
 
     /**
@@ -256,17 +239,12 @@
         /**
          * No padding.
          */
-        public static final int NONE = 0;
-
-        /**
-         * Pad with zeros.
-         */
-        public static final int ZERO = 1;
+        public static final int NONE = 1 << 0;
 
         /**
          * PKCS#7 padding.
          */
-        public static final int PKCS7 = 2;
+        public static final int PKCS7 = 1 << 1;
 
         /**
          * @hide
@@ -275,8 +253,6 @@
             switch (padding) {
                 case NONE:
                     return KeymasterDefs.KM_PAD_NONE;
-                case ZERO:
-                    return KeymasterDefs.KM_PAD_ZERO;
                 case PKCS7:
                     return KeymasterDefs.KM_PAD_PKCS7;
                 default:
@@ -291,8 +267,6 @@
             switch (padding) {
                 case KeymasterDefs.KM_PAD_NONE:
                     return NONE;
-                case KeymasterDefs.KM_PAD_ZERO:
-                    return ZERO;
                 case KeymasterDefs.KM_PAD_PKCS7:
                     return PKCS7;
                 default:
@@ -307,8 +281,6 @@
             switch (padding) {
                 case NONE:
                     return "NONE";
-                case ZERO:
-                    return "ZERO";
                 case PKCS7:
                     return "PKCS#7";
                 default:
@@ -329,10 +301,33 @@
                 throw new IllegalArgumentException("Unknown padding: " + padding);
             }
         }
+
+        /**
+         * @hide
+         */
+        public static int[] allToKeymaster(@PaddingEnum int paddings) {
+            int[] result = getSetFlags(paddings);
+            for (int i = 0; i < result.length; i++) {
+                result[i] = toKeymaster(result[i]);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int allFromKeymaster(Collection<Integer> paddings) {
+            @PaddingEnum int result = 0;
+            for (int keymasterPadding : paddings) {
+                result |= fromKeymaster(keymasterPadding);
+            }
+            return result;
+        }
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Digest.NONE, Digest.SHA256})
+    @IntDef(flag = true,
+            value = {Digest.NONE, Digest.SHA256})
     public @interface DigestEnum {}
 
     /**
@@ -345,12 +340,12 @@
         /**
          * No digest: sign/authenticate the raw message.
          */
-        public static final int NONE = 0;
+        public static final int NONE = 1 << 0;
 
         /**
          * SHA-256 digest.
          */
-        public static final int SHA256 = 1;
+        public static final int SHA256 = 1 << 1;
 
         /**
          * @hide
@@ -369,6 +364,18 @@
         /**
          * @hide
          */
+        public static String[] allToString(@DigestEnum int digests) {
+            int[] values = getSetFlags(digests);
+            String[] result = new String[values.length];
+            for (int i = 0; i < values.length; i++) {
+                result[i] = toString(values[i]);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
         public static int toKeymaster(@DigestEnum int digest) {
             switch (digest) {
                 case NONE:
@@ -397,6 +404,28 @@
         /**
          * @hide
          */
+        public static int[] allToKeymaster(@DigestEnum int digests) {
+            int[] result = getSetFlags(digests);
+            for (int i = 0; i < result.length; i++) {
+                result[i] = toKeymaster(result[i]);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static @DigestEnum int allFromKeymaster(Collection<Integer> digests) {
+            @DigestEnum int result = 0;
+            for (int keymasterDigest : digests) {
+                result |= fromKeymaster(keymasterDigest);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
         public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
             String algorithmLower = algorithm.toLowerCase(Locale.US);
             if (algorithmLower.startsWith("hmac")) {
@@ -441,7 +470,8 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+    @IntDef(flag = true,
+            value = {BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
     public @interface BlockModeEnum {}
 
     /**
@@ -451,13 +481,13 @@
         private BlockMode() {}
 
         /** Electronic Codebook (ECB) block mode. */
-        public static final int ECB = 0;
+        public static final int ECB = 1 << 0;
 
         /** Cipher Block Chaining (CBC) block mode. */
-        public static final int CBC = 1;
+        public static final int CBC = 1 << 1;
 
         /** Counter (CTR) block mode. */
-        public static final int CTR = 2;
+        public static final int CTR = 1 << 2;
 
         /**
          * @hide
@@ -494,6 +524,28 @@
         /**
          * @hide
          */
+        public static int[] allToKeymaster(@BlockModeEnum int modes) {
+            int[] result = getSetFlags(modes);
+            for (int i = 0; i < result.length; i++) {
+                result[i] = toKeymaster(result[i]);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int allFromKeymaster(Collection<Integer> modes) {
+            @BlockModeEnum int result = 0;
+            for (int keymasterMode : modes) {
+                result |= fromKeymaster(keymasterMode);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
         public static String toString(@BlockModeEnum int mode) {
             switch (mode) {
                 case ECB:
@@ -525,7 +577,8 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({UserAuthenticator.LOCK_SCREEN})
+    @IntDef(flag = true,
+            value = {UserAuthenticator.LOCK_SCREEN})
     public @interface UserAuthenticatorEnum {}
 
     /**
@@ -535,7 +588,7 @@
         private UserAuthenticator() {}
 
         /** Lock screen. */
-        public static final int LOCK_SCREEN = 1;
+        public static final int LOCK_SCREEN = 1 << 0;
 
         /** Fingerprint reader/sensor. */
         public static final int FINGERPRINT_READER = 1 << 1;
@@ -546,9 +599,9 @@
         public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
             switch (userAuthenticator) {
                 case LOCK_SCREEN:
-                    return LOCK_SCREEN;
+                    return KeymasterDefs.HW_AUTH_PASSWORD;
                 case FINGERPRINT_READER:
-                    return FINGERPRINT_READER;
+                    return KeymasterDefs.HW_AUTH_FINGERPRINT;
                 default:
                     throw new IllegalArgumentException(
                             "Unknown user authenticator: " + userAuthenticator);
@@ -560,7 +613,7 @@
          */
         public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
             switch (userAuthenticator) {
-                case LOCK_SCREEN:
+                case KeymasterDefs.HW_AUTH_PASSWORD:
                     return LOCK_SCREEN;
                 case FINGERPRINT_READER:
                     return FINGERPRINT_READER;
@@ -573,10 +626,15 @@
         /**
          * @hide
          */
-        public static int allToKeymaster(Set<Integer> userAuthenticators) {
+        public static int allToKeymaster(@UserAuthenticatorEnum int userAuthenticators) {
             int result = 0;
-            for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) {
-                result |= toKeymaster(userAuthenticator);
+            int userAuthenticator = 1;
+            while (userAuthenticators != 0) {
+                if ((userAuthenticators & 1) != 0) {
+                    result |= toKeymaster(userAuthenticator);
+                }
+                userAuthenticators >>>= 1;
+                userAuthenticator <<= 1;
             }
             return result;
         }
@@ -584,20 +642,17 @@
         /**
          * @hide
          */
-        public static Set<Integer> allFromKeymaster(int userAuthenticators) {
+        public static @UserAuthenticatorEnum int allFromKeymaster(int userAuthenticators) {
+            @UserAuthenticatorEnum int result = 0;
             int userAuthenticator = 1;
-            Set<Integer> result = null;
             while (userAuthenticators != 0) {
                 if ((userAuthenticators & 1) != 0) {
-                    if (result == null) {
-                        result = new HashSet<Integer>();
-                    }
-                    result.add(fromKeymaster(userAuthenticator));
+                    result |= fromKeymaster(userAuthenticator);
                 }
                 userAuthenticators >>>= 1;
                 userAuthenticator <<= 1;
             }
-            return (result != null) ? result : Collections.<Integer>emptySet();
+            return result;
         }
 
         /**
@@ -615,4 +670,38 @@
             }
         }
     }
+
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
+    private static int[] getSetFlags(int flags) {
+        if (flags == 0) {
+            return EMPTY_INT_ARRAY;
+        }
+        int result[] = new int[getSetBitCount(flags)];
+        int resultOffset = 0;
+        int flag = 1;
+        while (flags != 0) {
+            if ((flags & 1) != 0) {
+                result[resultOffset] = flag;
+                resultOffset++;
+            }
+            flags >>>= 1;
+            flag <<= 1;
+        }
+        return result;
+    }
+
+    private static int getSetBitCount(int value) {
+        if (value == 0) {
+            return 0;
+        }
+        int result = 0;
+        while (value != 0) {
+            if ((value & 1) != 0) {
+                result++;
+            }
+            value >>>= 1;
+        }
+        return result;
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 48b6d06..abce32d 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -109,32 +109,20 @@
         }
         int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
-        @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
-                ? spec.getPurposes()
-                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
-                        | KeyStoreKeyConstraints.Purpose.DECRYPT
-                        | KeyStoreKeyConstraints.Purpose.SIGN
-                        | KeyStoreKeyConstraints.Purpose.VERIFY);
+        int purposes = spec.getPurposes();
         for (int keymasterPurpose :
             KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
             args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
         }
-        if (spec.getBlockMode() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
-                    KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
+        for (int keymasterBlockMode :
+            KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
         }
-        if (spec.getPadding() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_PADDING,
-                    KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
+        for (int keymasterPadding :
+            KeyStoreKeyConstraints.Padding.allToKeymaster(spec.getPaddings())) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
         }
-        if (spec.getMaxUsesPerBoot() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
-        }
-        if (spec.getMinSecondsBetweenOperations() != null) {
-            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
-                    spec.getMinSecondsBetweenOperations());
-        }
-        if (spec.getUserAuthenticators().isEmpty()) {
+        if (spec.getUserAuthenticators() == 0) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
         } else {
             args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
@@ -145,7 +133,7 @@
             // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
             // that.
         }
-        if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() != -1) {
             args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     spec.getUserAuthenticationValidityDurationSeconds());
         }
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index e5e5acc..256d9b3 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -17,10 +17,7 @@
 package android.security;
 
 import java.security.spec.KeySpec;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
@@ -37,34 +34,31 @@
     private final Date mKeyValidityForConsumptionEnd;
     private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
     private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
-    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
-    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
-    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
-    private final Integer mMinSecondsBetweenOperations;
-    private final Integer mMaxUsesPerBoot;
-    private final Set<Integer> mUserAuthenticators;
-    private final Set<Integer> mTeeBackedUserAuthenticators;
-    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+    private final @KeyStoreKeyConstraints.DigestEnum int mDigests;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mTeeEnforcedUserAuthenticators;
+    private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mInvalidatedOnNewFingerprintEnrolled;
 
-
     /**
      * @hide
      */
     KeyStoreKeySpec(String keystoreKeyAlias,
             @KeyStoreKeyCharacteristics.OriginEnum int origin,
-            int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
+            int keySize,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
             @KeyStoreKeyConstraints.PurposeEnum int purposes,
             @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
-            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
-            @KeyStoreKeyConstraints.DigestEnum Integer digest,
-            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
-            Integer minSecondsBetweenOperations,
-            Integer maxUsesPerBoot,
-            Set<Integer> userAuthenticators,
-            Set<Integer> teeBackedUserAuthenticators,
-            Integer userAuthenticationValidityDurationSeconds,
+            @KeyStoreKeyConstraints.PaddingEnum int paddings,
+            @KeyStoreKeyConstraints.DigestEnum int digests,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators,
+            int userAuthenticationValidityDurationSeconds,
             boolean invalidatedOnNewFingerprintEnrolled) {
         mKeystoreAlias = keystoreKeyAlias;
         mOrigin = origin;
@@ -74,17 +68,11 @@
         mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
         mPurposes = purposes;
         mAlgorithm = algorithm;
-        mPadding = padding;
-        mDigest = digest;
-        mBlockMode = blockMode;
-        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
-        mMaxUsesPerBoot = maxUsesPerBoot;
-        mUserAuthenticators = (userAuthenticators != null)
-                ? new HashSet<Integer>(userAuthenticators)
-                : Collections.<Integer>emptySet();
-        mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
-                ? new HashSet<Integer>(teeBackedUserAuthenticators)
-                : Collections.<Integer>emptySet();
+        mPaddings = paddings;
+        mDigests = digests;
+        mBlockModes = blockModes;
+        mUserAuthenticators = userAuthenticators;
+        mTeeEnforcedUserAuthenticators = teeEnforcedUserAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
     }
@@ -104,7 +92,7 @@
     }
 
     /**
-     * Gets the key's size in bits.
+     * Gets the size of the key in bits.
      */
     public int getKeySize() {
         return mKeySize;
@@ -152,78 +140,53 @@
     }
 
     /**
-     * Gets the only block mode with which the key can be used.
-     *
-     * @return block mode or {@code null} if the block mode is not restricted.
+     * Gets the set of block modes with which the key can be used.
      */
-    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
-        return mBlockMode;
+    public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+        return mBlockModes;
     }
 
     /**
-     * Gets the only padding mode with which the key can be used.
-     *
-     * @return padding mode or {@code null} if the padding mode is not restricted.
+     * Gets the set of padding modes with which the key can be used.
      */
-    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
-        return mPadding;
+    public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+        return mPaddings;
     }
 
     /**
-     * Gets the only digest algorithm with which the key can be used.
-     *
-     * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
+     * Gets the set of digest algorithms with which the key can be used.
      */
-    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
-        return mDigest;
+    public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+        return mDigests;
     }
 
     /**
-     * Gets the minimum number of seconds that must expire since the most recent use of the key
-     * before it can be used again.
+     * Gets the set of user authenticators which protect access to the key. The key can only be used
+     * iff the user has authenticated to at least one of these user authenticators.
      *
-     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
-     *         can be used.
+     * @return user authenticators or {@code 0} if the key can be used without user authentication.
      */
-    public Integer getMinSecondsBetweenOperations() {
-        return mMinSecondsBetweenOperations;
+    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+        return mUserAuthenticators;
     }
 
     /**
-     * Gets the number of times the key can be used without rebooting the device.
-     *
-     * @return maximum number of times or {@code null} if there is no restriction.
+     * Gets the set of user authenticators for which the TEE enforces access restrictions for this
+     * key. This is a subset of the user authentications returned by
+     * {@link #getUserAuthenticators()}.
      */
-    public Integer getMaxUsesPerBoot() {
-        return mMaxUsesPerBoot;
-    }
-
-    /**
-     * Gets the user authenticators which protect access to the key. The key can only be used iff
-     * the user has authenticated to at least one of these user authenticators.
-     *
-     * @return user authenticators or empty set if the key can be used without user authentication.
-     */
-    public Set<Integer> getUserAuthenticators() {
-        return new HashSet<Integer>(mUserAuthenticators);
-    }
-
-    /**
-     * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
-     * the user authentications returned by {@link #getUserAuthenticators()}.
-     */
-    public Set<Integer> getTeeBackedUserAuthenticators() {
-        return new HashSet<Integer>(mTeeBackedUserAuthenticators);
+    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getTeeEnforcedUserAuthenticators() {
+        return mTeeEnforcedUserAuthenticators;
     }
 
     /**
      * Gets the duration of time (seconds) for which the key can be used after the user
      * successfully authenticates to one of the associated user authenticators.
      *
-     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
      *         is required for every use of the key.
      */
-    public Integer getUserAuthenticationValidityDurationSeconds() {
+    public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
     }
 
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 88bd6b4..0b2f9b6 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -18,12 +18,10 @@
 
 import android.content.Context;
 
+import java.security.Key;
 import java.security.KeyPairGenerator;
 import java.security.KeyStore.ProtectionParameter;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * This provides the optional parameters that can be specified for
@@ -50,33 +48,27 @@
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
-    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
-    private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
-    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
-    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
-    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
-    private final Integer mMinSecondsBetweenOperations;
-    private final Integer mMaxUsesPerBoot;
-    private final Set<Integer> mUserAuthenticators;
-    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+    private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mInvalidatedOnNewFingerprintEnrolled;
 
     private KeyStoreParameter(int flags,
             Date keyValidityStart,
             Date keyValidityForOriginationEnd,
             Date keyValidityForConsumptionEnd,
-            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
-            @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm,
-            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
-            @KeyStoreKeyConstraints.DigestEnum Integer digest,
-            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
-            Integer minSecondsBetweenOperations,
-            Integer maxUsesPerBoot,
-            Set<Integer> userAuthenticators,
-            Integer userAuthenticationValidityDurationSeconds,
+            @KeyStoreKeyConstraints.PurposeEnum int purposes,
+            @KeyStoreKeyConstraints.PaddingEnum int paddings,
+            @KeyStoreKeyConstraints.DigestEnum Integer digests,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
+            int userAuthenticationValidityDurationSeconds,
             boolean invalidatedOnNewFingerprintEnrolled) {
-        if ((userAuthenticationValidityDurationSeconds != null)
-                && (userAuthenticationValidityDurationSeconds < 0)) {
+        if ((userAuthenticationValidityDurationSeconds < 0)
+                && (userAuthenticationValidityDurationSeconds != -1)) {
             throw new IllegalArgumentException(
                     "userAuthenticationValidityDurationSeconds must not be negative");
         }
@@ -86,15 +78,10 @@
         mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
         mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
         mPurposes = purposes;
-        mAlgorithm = algorithm;
-        mPadding = padding;
-        mDigest = digest;
-        mBlockMode = blockMode;
-        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
-        mMaxUsesPerBoot = maxUsesPerBoot;
-        mUserAuthenticators = (userAuthenticators != null)
-                ? new HashSet<Integer>(userAuthenticators)
-                : Collections.<Integer>emptySet();
+        mPaddings = paddings;
+        mDigests = digests;
+        mBlockModes = blockModes;
+        mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
     }
@@ -147,105 +134,81 @@
     }
 
     /**
-     * Gets the set of purposes for which the key can be used to the provided set of purposes.
-     *
-     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     * Gets the set of purposes for which the key can be used.
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
         return mPurposes;
     }
 
     /**
-     * Gets the algorithm to which the key is restricted.
+     * Gets the set of padding schemes to which the key is restricted.
      *
-     * @return algorithm or {@code null} if it's not restricted.
      * @hide
      */
-    public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() {
-        return mAlgorithm;
+    public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
+        return mPaddings;
     }
 
     /**
-     * Gets the padding scheme to which the key is restricted.
+     * Gets the set of digests to which the key is restricted.
      *
-     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     * @throws IllegalStateException if this restriction has not been specified.
+     *
+     * @see #isDigestsSpecified()
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
-        return mPadding;
+    public @KeyStoreKeyConstraints.DigestEnum int getDigests() {
+        if (mDigests == null) {
+            throw new IllegalStateException("Digests not specified");
+        }
+        return mDigests;
     }
 
     /**
-     * Gets the digest to which the key is restricted when generating signatures or Message
-     * Authentication Codes (MACs).
+     * Returns {@code true} if digest restrictions have been specified.
      *
-     * @return digest or {@code null} if the digest is not restricted.
+     * @see #getDigests()
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
-        return mDigest;
+    public boolean isDigestsSpecified() {
+        return mDigests != null;
     }
 
     /**
-     * Gets the block mode to which the key is restricted when used for encryption or decryption.
-     *
-     * @return block more or {@code null} if block mode is not restricted.
+     * Gets the set of block modes to which the key is restricted.
      *
      * @hide
      */
-    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
-        return mBlockMode;
+    public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
+        return mBlockModes;
     }
 
     /**
-     * Gets the minimum number of seconds that must expire since the most recent use of the key
-     * before it can be used again.
+     * Gets the set of user authenticators which protect access to this key. The key can only be
+     * used iff the user has authenticated to at least one of these user authenticators.
      *
-     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
-     *         can be used.
+     * @return user authenticators or {@code 0} if the key can be used without user authentication.
      *
      * @hide
      */
-    public Integer getMinSecondsBetweenOperations() {
-        return mMinSecondsBetweenOperations;
-    }
-
-    /**
-     * Gets the number of times the key can be used without rebooting the device.
-     *
-     * @return maximum number of times or {@code null} if there is no restriction.
-     * @hide
-     */
-    public Integer getMaxUsesPerBoot() {
-        return mMaxUsesPerBoot;
-    }
-
-    /**
-     * Gets the user authenticators which protect access to this key. The key can only be used iff
-     * the user has authenticated to at least one of these user authenticators.
-     *
-     * @return user authenticators or empty set if the key can be used without user authentication.
-     *
-     * @hide
-     */
-    public Set<Integer> getUserAuthenticators() {
-        return new HashSet<Integer>(mUserAuthenticators);
+    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
+        return mUserAuthenticators;
     }
 
     /**
      * Gets the duration of time (seconds) for which this key can be used after the user
      * successfully authenticates to one of the associated user authenticators.
      *
-     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
      *         is required for every use of the key.
      *
      * @hide
      */
-    public Integer getUserAuthenticationValidityDurationSeconds() {
+    public int getUserAuthenticationValidityDurationSeconds() {
         return mUserAuthenticationValidityDurationSeconds;
     }
 
@@ -284,15 +247,12 @@
         private Date mKeyValidityStart;
         private Date mKeyValidityForOriginationEnd;
         private Date mKeyValidityForConsumptionEnd;
-        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
-        private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
-        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
-        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
-        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
-        private Integer mMinSecondsBetweenOperations;
-        private Integer mMaxUsesPerBoot;
-        private Set<Integer> mUserAuthenticators;
-        private Integer mUserAuthenticationValidityDurationSeconds;
+        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
+        private @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
+        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
+        private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mInvalidatedOnNewFingerprintEnrolled;
 
         /**
@@ -385,9 +345,9 @@
         }
 
         /**
-         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         * Restricts the key to being used only for the provided set of purposes.
          *
-         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         * <p>This restriction must be specified. There is no default.
          *
          * @hide
          */
@@ -397,84 +357,43 @@
         }
 
         /**
-         * Sets the algorithm of the key.
-         *
-         * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly
-         * specifying the algorithm of symmetric keys using this method is not necessary.
-         *
-         * @hide
-         */
-        public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) {
-            mAlgorithm = algorithm;
-            return this;
-        }
-
-        /**
-         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * Restricts the key to being used only with the provided padding schemes. Attempts to use
          * the key with any other padding will be rejected.
          *
          * <p>This restriction must be specified for keys which are used for encryption/decryption.
          *
          * @hide
          */
-        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
-            mPadding = padding;
+        public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
+            mPaddings = paddings;
             return this;
         }
 
         /**
-         * Restricts the key to being used only with the provided digest when generating signatures
-         * or Message Authentication Codes (MACs). Attempts to use the key with any other digest
-         * will be rejected.
+         * Restricts the key to being used only with the provided digests when generating signatures
+         * or HMACs. Attempts to use the key with any other digest will be rejected.
          *
-         * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm
-         * name. For asymmetric signing keys this constraint must be specified because there is no
-         * default.
-         *
-         * @see java.security.Key#getAlgorithm()
+         * <p>For HMAC keys, the default is to restrict to the digest specified in
+         * {@link Key#getAlgorithm()}. For asymmetric signing keys this constraint must be specified
+         * because there is no default.
          *
          * @hide
          */
-        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
-            mDigest = digest;
+        public Builder setDigests(@KeyStoreKeyConstraints.DigestEnum int digests) {
+            mDigests = digests;
             return this;
         }
 
         /**
-         * Restricts the key to being used only with the provided block mode when encrypting or
-         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         * Restricts the key to being used only with the provided block modes. Attempts to use the
+         * key with any other block modes will be rejected.
          *
-         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         * <p>This restriction must be specified for symmetric encryption/decryption keys.
          *
          * @hide
          */
-        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
-            mBlockMode = blockMode;
-            return this;
-        }
-
-        /**
-         * Sets the minimum number of seconds that must expire since the most recent use of the key
-         * before it can be used again.
-         *
-         * <p>By default, there is no restriction on how frequently a key can be used.
-         *
-         * @hide
-         */
-        public Builder setMinSecondsBetweenOperations(int seconds) {
-            mMinSecondsBetweenOperations = seconds;
-            return this;
-        }
-
-        /**
-         * Sets the maximum number of times a key can be used without rebooting the device.
-         *
-         * <p>By default, the key can be used for an unlimited number of times.
-         *
-         * @hide
-         */
-        public Builder setMaxUsesPerBoot(int count) {
-            mMaxUsesPerBoot = count;
+        public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
+            mBlockModes = blockModes;
             return this;
         }
 
@@ -484,16 +403,16 @@
          *
          * <p>By default, the key can be used without user authentication.
          *
-         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         * @param userAuthenticators user authenticators or {@code 0} if this key can be accessed
          *        without user authentication.
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          *
          * @hide
          */
-        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
-            mUserAuthenticators =
-                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+        public Builder setUserAuthenticators(
+                @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
+            mUserAuthenticators = userAuthenticators;
             return this;
         }
 
@@ -506,7 +425,7 @@
          * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
          *        every use of the key.
          *
-         * @see #setUserAuthenticators(Set)
+         * @see #setUserAuthenticators(int)
          *
          * @hide
          */
@@ -543,12 +462,9 @@
                     mKeyValidityForOriginationEnd,
                     mKeyValidityForConsumptionEnd,
                     mPurposes,
-                    mAlgorithm,
-                    mPadding,
-                    mDigest,
-                    mBlockMode,
-                    mMinSecondsBetweenOperations,
-                    mMaxUsesPerBoot,
+                    mPaddings,
+                    mDigests,
+                    mBlockModes,
                     mUserAuthenticators,
                     mUserAuthenticationValidityDurationSeconds,
                     mInvalidatedOnNewFingerprintEnrolled);
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index c205d9d..8bf228a 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -23,7 +23,6 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 import java.util.Date;
-import java.util.Set;
 
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactorySpi;
@@ -75,9 +74,11 @@
         int keySize;
         @KeyStoreKeyConstraints.PurposeEnum int purposes;
         @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
-        @KeyStoreKeyConstraints.PaddingEnum Integer padding;
-        @KeyStoreKeyConstraints.DigestEnum Integer digest;
-        @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
+        @KeyStoreKeyConstraints.PaddingEnum int paddings;
+        @KeyStoreKeyConstraints.DigestEnum int digests;
+        @KeyStoreKeyConstraints.BlockModeEnum int blockModes;
+        @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators;
+        @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators;
         try {
             origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
             if (origin == null) {
@@ -97,18 +98,27 @@
                 throw new InvalidKeySpecException("Key algorithm not available");
             }
             algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
-            padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
-            if (padding != null) {
-                padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
-            }
-            digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
-            if (digest != null) {
-                digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
-            }
-            blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
-            if (blockMode != null) {
-                blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
-            }
+            paddings = KeyStoreKeyConstraints.Padding.allFromKeymaster(
+                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING));
+            digests = KeyStoreKeyConstraints.Digest.allFromKeymaster(
+                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST));
+            blockModes = KeyStoreKeyConstraints.BlockMode.allFromKeymaster(
+                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE));
+
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum
+            int swEnforcedKeymasterUserAuthenticators =
+                    keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum
+            int hwEnforcedKeymasterUserAuthenticators =
+                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+            @KeyStoreKeyConstraints.UserAuthenticatorEnum
+            int keymasterUserAuthenticators =
+                    swEnforcedKeymasterUserAuthenticators | hwEnforcedKeymasterUserAuthenticators;
+            userAuthenticators = KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+                    keymasterUserAuthenticators);
+            teeEnforcedUserAuthenticators =
+                    KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+                            hwEnforcedKeymasterUserAuthenticators);
         } catch (IllegalArgumentException e) {
             throw new InvalidKeySpecException("Unsupported key characteristic", e);
         }
@@ -130,17 +140,8 @@
                 && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) {
             keyValidityForConsumptionEnd = null;
         }
-
-        int swEnforcedUserAuthenticatorIds =
-                keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
-        int hwEnforcedUserAuthenticatorIds =
-                keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
-        int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds;
-        Set<Integer> userAuthenticators =
-                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds);
-        Set<Integer> teeBackedUserAuthenticators =
-                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
-                        hwEnforcedUserAuthenticatorIds);
+        Integer userAuthenticationValidityDurationSeconds =
+                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT);
 
         // TODO: Populate the value below from key characteristics once Keymaster is ready.
         boolean invalidatedOnNewFingerprintEnrolled = false;
@@ -153,15 +154,13 @@
                 keyValidityForConsumptionEnd,
                 purposes,
                 algorithm,
-                padding,
-                digest,
-                blockMode,
-                KeymasterUtils.getInt(keyCharacteristics,
-                        KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
-                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
+                paddings,
+                digests,
+                blockModes,
                 userAuthenticators,
-                teeBackedUserAuthenticators,
-                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT),
+                teeEnforcedUserAuthenticators,
+                ((userAuthenticationValidityDurationSeconds != null)
+                        ? userAuthenticationValidityDurationSeconds : -1),
                 invalidatedOnNewFingerprintEnrolled);
     }
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index a7f33fa..fd7fca6 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -540,6 +540,8 @@
      * or the flags are not set properly
      * (e.g. missing {@link #CONFIGURE_FLAG_ENCODE} for an encoder).
      * @throws IllegalStateException if not in the Initialized state.
+     * @throws CryptoException upon DRM error.
+     * @throws CodecException upon codec error.
      */
     public void configure(
             MediaFormat format,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index ff6a45a..64730c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.view.LayoutInflater;
@@ -46,20 +47,17 @@
     private final DndDetailAdapter mDetailAdapter;
 
     private boolean mListening;
-    private boolean mVisible;
     private boolean mShowingDetail;
 
     public DndTile(Host host) {
         super(host);
         mController = host.getZenModeController();
         mDetailAdapter = new DndDetailAdapter();
-        mVisible = isVisible(host.getContext());
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
     }
 
     public static void setVisible(Context context, boolean visible) {
-        context.sendBroadcast(new Intent(DndTile.ACTION_SET_VISIBLE)
-                .putExtra(DndTile.EXTRA_VISIBLE, visible));
+        getSharedPrefs(context).edit().putBoolean(PREF_KEY_VISIBLE, visible).commit();
     }
 
     public static boolean isVisible(Context context) {
@@ -98,7 +96,7 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
         state.value = zen != Global.ZEN_MODE_OFF;
-        state.visible = mVisible;
+        state.visible = isVisible(mContext);
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
@@ -145,26 +143,38 @@
         mListening = listening;
         if (mListening) {
             mController.addCallback(mZenCallback);
+            getSharedPrefs(mContext).registerOnSharedPreferenceChangeListener(mPrefListener);
         } else {
             mController.removeCallback(mZenCallback);
+            getSharedPrefs(mContext).unregisterOnSharedPreferenceChangeListener(mPrefListener);
         }
     }
 
+    private static SharedPreferences getSharedPrefs(Context context) {
+        return context.getSharedPreferences(context.getPackageName(), 0);
+    }
+
+    private final OnSharedPreferenceChangeListener mPrefListener
+            = new OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            if (PREF_KEY_COMBINED_ICON.equals(key) || PREF_KEY_VISIBLE.equals(key)) {
+                refreshState();
+            }
+        }
+    };
+
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         public void onZenChanged(int zen) {
             refreshState(zen);
         }
     };
 
-    private static SharedPreferences getSharedPrefs(Context context) {
-        return context.getSharedPreferences(context.getPackageName(), 0);
-    }
-
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            mVisible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
-            getSharedPrefs(mContext).edit().putBoolean(PREF_KEY_VISIBLE, mVisible).commit();
+            final boolean visible = intent.getBooleanExtra(EXTRA_VISIBLE, false);
+            setVisible(mContext, visible);
             refreshState();
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 3b8fccc..cab152f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -69,7 +69,7 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             mSwipeAnimator = null;
-            setSwipingInProgress(false);
+            mSwipingInProgress = false;
         }
     };
     private Runnable mAnimationEndRunnable = new Runnable() {
@@ -117,14 +117,17 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        if (mMotionCancelled && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+        int action = event.getActionMasked();
+        if (mMotionCancelled && action != MotionEvent.ACTION_DOWN
+                && action != MotionEvent.ACTION_UP
+                && action != MotionEvent.ACTION_CANCEL) {
             return false;
         }
         final float y = event.getY();
         final float x = event.getX();
 
         boolean isUp = false;
-        switch (event.getActionMasked()) {
+        switch (action) {
             case MotionEvent.ACTION_DOWN:
                 if (mSwipingInProgress) {
                     cancelAnimation();
@@ -152,7 +155,8 @@
                     mInitialTouchY = y;
                     mInitialTouchX = x;
                     mTranslationOnDown = mTranslation;
-                    setSwipingInProgress(true);
+                    mSwipingInProgress = true;
+                    mCallback.onSwipingStarted(w < -mTouchSlop);
                 }
                 if (mSwipingInProgress) {
                     setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false);
@@ -179,13 +183,6 @@
         }
     }
 
-    private void setSwipingInProgress(boolean inProgress) {
-        mSwipingInProgress = inProgress;
-        if (inProgress) {
-            mCallback.onSwipingStarted();
-        }
-    }
-
     private boolean rightSwipePossible() {
         return mRightIcon.getVisibility() == View.VISIBLE;
     }
@@ -323,6 +320,9 @@
         }
         animator.start();
         mSwipeAnimator = animator;
+        if (snapBack) {
+            mCallback.onSwipingAborted();
+        }
     }
 
     private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
@@ -451,7 +451,11 @@
             mSwipeAnimator.cancel();
         }
         setTranslation(0.0f, true, animate);
-        setSwipingInProgress(false);
+        mMotionCancelled = true;
+        if (mSwipingInProgress) {
+            mCallback.onSwipingAborted();
+        }
+        mSwipingInProgress = false;
     }
 
     public interface Callback {
@@ -470,7 +474,9 @@
 
         float getPageWidth();
 
-        void onSwipingStarted();
+        void onSwipingStarted(boolean isRightwardMotion);
+
+        void onSwipingAborted();
 
         KeyguardAffordanceView getLeftIcon();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a247c8e..628ae84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -100,6 +100,7 @@
     private final TrustDrawable mTrustDrawable;
     private final Interpolator mLinearOutSlowInInterpolator;
     private int mLastUnlockIconRes = 0;
+    private boolean mPrewarmSent;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -335,12 +336,47 @@
         mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
     }
 
-    public void launchCamera() {
+    public void prewarmCamera() {
         Intent intent = getCameraIntent();
+        String targetPackage = PreviewInflater.getTargetPackage(mContext, intent,
+                mLockPatternUtils.getCurrentUser());
+        if (targetPackage != null) {
+            Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM);
+            prewarm.setPackage(targetPackage);
+            mPrewarmSent = true;
+            mContext.sendBroadcast(prewarm);
+        }
+    }
+
+    public void maybeCooldownCamera() {
+        if (!mPrewarmSent) {
+            return;
+        }
+        mPrewarmSent = false;
+        Intent intent = getCameraIntent();
+        String targetPackage = PreviewInflater.getTargetPackage(mContext, intent,
+                mLockPatternUtils.getCurrentUser());
+        if (targetPackage != null) {
+            Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN);
+            prewarm.setPackage(targetPackage);
+            mContext.sendBroadcast(prewarm);
+        }
+    }
+
+    public void launchCamera() {
+
+        // Reset prewarm state.
+        mPrewarmSent = false;
+        final Intent intent = getCameraIntent();
         boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, mLockPatternUtils.getCurrentUser());
         if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+            AsyncTask.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                }
+            });
         } else {
 
             // We need to delay starting the activity because ResolverActivity finishes itself if
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 195da46..216730b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1809,14 +1809,24 @@
     }
 
     @Override
-    public void onSwipingStarted() {
-        mSecureCameraLaunchManager.onSwipingStarted();
+    public void onSwipingStarted(boolean isRightwardMotion) {
+        boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? isRightwardMotion
+                : !isRightwardMotion;
+        if (!start) {
+            mSecureCameraLaunchManager.onSwipingStarted();
+            mKeyguardBottomArea.prewarmCamera();
+        }
         requestDisallowInterceptTouchEvent(true);
         mOnlyAffordanceInThisMotion = true;
         mQsTracking = false;
     }
 
     @Override
+    public void onSwipingAborted() {
+        mKeyguardBottomArea.maybeCooldownCamera();
+    }
+
+    @Override
     public KeyguardAffordanceView getLeftIcon() {
         return getLayoutDirection() == LAYOUT_DIRECTION_RTL
                 ? mKeyguardBottomArea.getCameraView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
index 34068fd..0dce82f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -106,15 +107,28 @@
 
     public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
             int currentUserId) {
+        return getTargetPackage(ctx, intent, currentUserId) == null;
+    }
+
+    /**
+     * @return the target package of the intent it resolves to a specific package or {@code null} if
+     *         it resolved to the resolver activity
+     */
+    public static String getTargetPackage(Context ctx, Intent intent,
+            int currentUserId) {
         PackageManager packageManager = ctx.getPackageManager();
         final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
                 intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId);
         if (appList.size() == 0) {
-            return false;
+            return null;
         }
         ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
                 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId);
-        return wouldLaunchResolverActivity(resolved, appList);
+        if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
+            return null;
+        } else {
+            return resolved.activityInfo.packageName;
+        }
     }
 
     private static boolean wouldLaunchResolverActivity(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index a3d9377..265e2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -45,6 +45,7 @@
 import android.util.SparseArray;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.tiles.DndTile;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -489,8 +490,7 @@
 
     public void showDndTile(boolean visible) {
         if (D.BUG) Log.d(TAG, "showDndTile");
-        mContext.sendBroadcast(new Intent("com.android.systemui.dndtile.SET_VISIBLE")
-                .putExtra("visible", visible));
+        DndTile.setVisible(mContext, visible);
     }
 
     private final class VC extends IVolumeController.Stub {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1ac1c8a..eb394c3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1487,7 +1487,7 @@
                 NetworkCapabilities.TRANSPORT_WIFI)) {
             timeout = Settings.Global.getInt(mContext.getContentResolver(),
                                              Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
-                                             5);
+                                             0);
             type = ConnectivityManager.TYPE_WIFI;
         } else {
             // do not track any other networks
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 456f1fb..a341c95 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -833,6 +833,9 @@
             vol.userId = UserHandle.USER_OWNER;
             mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
+        } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+
         } else {
             Slog.d(TAG, "Skipping automatic mounting of " + vol);
         }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index b5b62b4..7b542be 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -210,7 +210,6 @@
 
     private boolean mMobileActivityFromRadio = false;
     private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
-    private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
 
     private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
             new RemoteCallbackList<INetworkActivityListener>();
@@ -435,16 +434,6 @@
             }
         }
 
-        if (ConnectivityManager.isNetworkTypeWifi(type)) {
-            if (mLastPowerStateFromWifi != powerState) {
-                mLastPowerStateFromWifi = powerState;
-                try {
-                    getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos);
-                } catch (RemoteException e) {
-                }
-            }
-        }
-
         boolean isActive = powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
                 || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4fa0577..e92443c 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2294,7 +2294,7 @@
                 EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
                         sr.userId, sr.crashCount, sr.shortName, app.pid);
                 bringDownServiceLocked(sr);
-            } else if (!allowRestart) {
+            } else if (!allowRestart || !mAm.isUserRunningLocked(sr.userId, false)) {
                 bringDownServiceLocked(sr);
             } else {
                 boolean canceled = scheduleServiceRestartLocked(sr, true);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b589142..18ab3b4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -41,7 +41,6 @@
 import android.app.IAppTask;
 import android.app.ITaskStackListener;
 import android.app.ProfilerInfo;
-import android.app.admin.DevicePolicyManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
@@ -429,6 +428,11 @@
      */
     ActivityInfo mLastAddedTaskActivity;
 
+    /**
+     * List of packages whitelisted by DevicePolicyManager for locktask. Indexed by userId.
+     */
+    SparseArray<String[]> mLockTaskPackages = new SparseArray<>();
+
     public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
         public final Bundle extras;
@@ -2217,7 +2221,7 @@
         mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
 
         mConfiguration.setToDefaults();
-        mConfiguration.locale = Locale.getDefault();
+        mConfiguration.setLocale(Locale.getDefault());
 
         mConfigurationSeq = mConfiguration.seq = 1;
         mProcessCpuTracker.init();
@@ -8463,52 +8467,54 @@
         }
     }
 
-    private boolean isLockTaskAuthorized(String pkg) {
-        final DevicePolicyManager dpm = (DevicePolicyManager)
-                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        try {
-            int uid = mContext.getPackageManager().getPackageUid(pkg,
-                    Binder.getCallingUserHandle().getIdentifier());
-            return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg);
-        } catch (NameNotFoundException e) {
-            return false;
+    @Override
+    public void updateLockTaskPackages(int userId, String[] packages) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("updateLockTaskPackage called from non-system process");
+        }
+        synchronized (this) {
+            mLockTaskPackages.put(userId, packages);
         }
     }
 
-    void startLockTaskMode(TaskRecord task) {
-        final String pkg;
-        synchronized (this) {
-            pkg = task.intent.getComponent().getPackageName();
+    private boolean isLockTaskAuthorizedLocked(String pkg) {
+        String[] packages = mLockTaskPackages.get(mCurrentUserId);
+        if (packages == null) {
+            return false;
         }
+        for (int i = packages.length - 1; i >= 0; --i) {
+            if (pkg.equals(packages[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void startLockTaskModeLocked(TaskRecord task) {
+        final String pkg = task.intent.getComponent().getPackageName();
         // isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
         // is initiated by system after the pinning request was shown and locked mode is initiated
         // by an authorized app directly
         boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
-        if (!isSystemInitiated && !isLockTaskAuthorized(pkg)) {
-            StatusBarManagerInternal statusBarManager = LocalServices.getService(
-                    StatusBarManagerInternal.class);
-            if (statusBarManager != null) {
-                statusBarManager.showScreenPinningRequest();
-            }
-            return;
-        }
         long ident = Binder.clearCallingIdentity();
         try {
-            synchronized (this) {
-                // Since we lost lock on task, make sure it is still there.
-                task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
-                if (task != null) {
-                    if (!isSystemInitiated
-                            && ((mStackSupervisor.getFocusedStack() == null)
-                                    || (task != mStackSupervisor.getFocusedStack().topTask()))) {
-                        throw new IllegalArgumentException("Invalid task, not in foreground");
-                    }
-                    mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
-                            ActivityManager.LOCK_TASK_MODE_PINNED :
-                            ActivityManager.LOCK_TASK_MODE_LOCKED,
-                            "startLockTask");
+            if (!isSystemInitiated && !isLockTaskAuthorizedLocked(pkg)) {
+                StatusBarManagerInternal statusBarManager =
+                        LocalServices.getService(StatusBarManagerInternal.class);
+                if (statusBarManager != null) {
+                    statusBarManager.showScreenPinningRequest();
                 }
+                return;
             }
+
+            final ActivityStack stack = mStackSupervisor.getFocusedStack();
+            if (!isSystemInitiated && (stack == null || task != stack.topTask())) {
+                throw new IllegalArgumentException("Invalid task, not in foreground");
+            }
+            mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated ?
+                    ActivityManager.LOCK_TASK_MODE_PINNED :
+                    ActivityManager.LOCK_TASK_MODE_LOCKED,
+                    "startLockTask");
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -8516,37 +8522,25 @@
 
     @Override
     public void startLockTaskMode(int taskId) {
-        final TaskRecord task;
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                task = mStackSupervisor.anyTaskForIdLocked(taskId);
+        synchronized (this) {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task != null) {
+                startLockTaskModeLocked(task);
             }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        if (task != null) {
-            startLockTaskMode(task);
         }
     }
 
     @Override
     public void startLockTaskMode(IBinder token) {
-        final TaskRecord task;
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-                if (r == null) {
-                    return;
-                }
-                task = r.task;
+        synchronized (this) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
             }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        if (task != null) {
-            startLockTaskMode(task);
+            final TaskRecord task = r.task;
+            if (task != null) {
+                startLockTaskModeLocked(task);
+            }
         }
     }
 
@@ -8556,11 +8550,12 @@
                 "startLockTaskModeOnCurrent");
         long ident = Binder.clearCallingIdentity();
         try {
-            ActivityRecord r = null;
             synchronized (this) {
-                r = mStackSupervisor.topRunningActivityLocked();
+                ActivityRecord r = mStackSupervisor.topRunningActivityLocked();
+                if (r != null) {
+                    startLockTaskModeLocked(r.task);
+                }
             }
-            startLockTaskMode(r.task);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1fee1ae..ce510a9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1270,6 +1270,7 @@
         // make sure any activities under it are now visible.
         boolean aboveTop = true;
         boolean behindFullscreen = !isStackVisibleLocked();
+        boolean noStackActivityResumed = (isInStackLocked(starting) == null);
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1297,9 +1298,8 @@
                     }
 
                     if (r.app == null || r.app.thread == null) {
-                        // This activity needs to be visible, but isn't even
-                        // running...  get it started, but don't resume it
-                        // at this point.
+                        // This activity needs to be visible, but isn't even running...
+                        // get it started and resume if no other stack in this stack is resumed.
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                 "Start and freeze screen for " + r);
                         if (r != starting) {
@@ -1311,7 +1311,9 @@
                             setVisible(r, true);
                         }
                         if (r != starting) {
-                            mStackSupervisor.startSpecificActivityLocked(r, false, false);
+                            mStackSupervisor.startSpecificActivityLocked(
+                                    r, noStackActivityResumed, false);
+                            noStackActivityResumed = false;
                         }
 
                     } else if (r.visible) {
@@ -4046,16 +4048,18 @@
     }
 
     void getTasksLocked(List<RunningTaskInfo> list, int callingUid, boolean allowed) {
+        boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
+        boolean topTask = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.getTopActivity() == null) {
+                continue;
+            }
             ActivityRecord r = null;
             ActivityRecord top = null;
             int numActivities = 0;
             int numRunning = 0;
             final ArrayList<ActivityRecord> activities = task.mActivities;
-            if (activities.isEmpty()) {
-                continue;
-            }
             if (!allowed && !task.isHomeTask() && task.effectiveUid != callingUid) {
                 continue;
             }
@@ -4084,14 +4088,18 @@
             ci.baseActivity = r.intent.getComponent();
             ci.topActivity = top.intent.getComponent();
             ci.lastActiveTime = task.lastActiveTime;
+            if (focusedStack && topTask) {
+                // Give the latest time to ensure foreground task can be sorted
+                // at the first, because lastActiveTime of creating task is 0.
+                ci.lastActiveTime = System.currentTimeMillis();
+                topTask = false;
+            }
 
             if (top.task != null) {
                 ci.description = top.task.lastDescription;
             }
             ci.numActivities = numActivities;
             ci.numRunning = numRunning;
-            //System.out.println(
-            //    "#" + maxNum + ": " + " descr=" + ci.description);
             list.add(ci);
         }
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ac70d88..c8db3be 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,7 +40,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -123,7 +122,6 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
-        mStats.setPowerProfile(new PowerProfile(context));
     }
 
     /**
@@ -543,15 +541,6 @@
         }
     }
 
-    @Override
-    public void noteWifiRadioPowerState(int powerState, long tsNanos) {
-        enforceCallingPermission();
-
-        // There was a change in WiFi power state.
-        // Collect data now for the past activity.
-        mHandler.scheduleSync();
-    }
-
     public void noteWifiRunning(WorkSource ws) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -1106,29 +1095,13 @@
                 result.mTimestamp = info.getTimeStamp();
                 result.mStackState = info.getStackState();
                 result.mControllerTxTimeMs =
-                        info.mControllerTxTimeMs - mLastInfo.mControllerTxTimeMs;
+                        info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs;
                 result.mControllerRxTimeMs =
-                        info.mControllerRxTimeMs - mLastInfo.mControllerRxTimeMs;
-                result.mControllerEnergyUsed =
-                        info.mControllerEnergyUsed - mLastInfo.mControllerEnergyUsed;
-
-                // WiFi calculates the idle time as a difference from the on time and the various
-                // Rx + Tx times. There seems to be some missing time there because this sometimes
-                // becomes negative. Just cap it at 0 and move on.
+                        info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs;
                 result.mControllerIdleTimeMs =
-                        Math.max(0, info.mControllerIdleTimeMs - mLastInfo.mControllerIdleTimeMs);
-
-                if (result.mControllerTxTimeMs < 0 ||
-                        result.mControllerRxTimeMs < 0) {
-                    // The stats were reset by the WiFi system (which is why our delta is negative).
-                    // Returns the unaltered stats.
-                    result.mControllerEnergyUsed = info.mControllerEnergyUsed;
-                    result.mControllerRxTimeMs = info.mControllerRxTimeMs;
-                    result.mControllerTxTimeMs = info.mControllerTxTimeMs;
-                    result.mControllerIdleTimeMs = info.mControllerIdleTimeMs;
-
-                    Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
-                }
+                        info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs;
+                result.mControllerEnergyUsed =
+                        info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed;
                 mLastInfo = info;
                 return result;
             }
@@ -1160,12 +1133,6 @@
      */
     void updateExternalStats() {
         synchronized (mExternalStatsLock) {
-            if (mContext == null) {
-                // We haven't started yet (which means the BatteryStatsImpl object has
-                // no power profile. Don't consume data we can't compute yet.
-                return;
-            }
-
             final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
             final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
             synchronized (mStats) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f05e6aa..4ccb5ad 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -63,8 +63,10 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.Stack;
+import java.text.DateFormat;
 
 /**
  * @hide
@@ -124,6 +126,8 @@
     }
 
     protected void dump(PrintWriter pw) {
+        pw.println("\nMediaFocusControl dump time: "
+                + DateFormat.getTimeInstance().format(new Date()));
         dumpFocusStack(pw);
         dumpRCStack(pw);
         dumpRCCStack(pw);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 95d7a52..591dbee 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -31,16 +31,22 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PackageDeleteObserver;
 import android.app.PackageInstallObserver;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionParams;
@@ -63,6 +69,8 @@
 import android.os.SELinux;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -75,15 +83,17 @@
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 
+import libcore.io.IoUtils;
+
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.ImageUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.google.android.collect.Sets;
 
-import libcore.io.IoUtils;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -131,6 +141,7 @@
     private static final String ATTR_ORIGINATING_URI = "originatingUri";
     private static final String ATTR_REFERRER_URI = "referrerUri";
     private static final String ATTR_ABI_OVERRIDE = "abiOverride";
+    private static final String ATTR_VOLUME_UUID = "volumeUuid";
 
     /** Automatically destroy sessions older than this */
     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -141,9 +152,10 @@
 
     private final Context mContext;
     private final PackageManagerService mPm;
-    private final AppOpsManager mAppOps;
 
-    private final File mStagingDir;
+    private AppOpsManager mAppOps;
+    private StorageManager mStorage;
+
     private final HandlerThread mInstallThread;
     private final Handler mInstallHandler;
 
@@ -186,12 +198,9 @@
         }
     };
 
-    public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
+    public PackageInstallerService(Context context, PackageManagerService pm) {
         mContext = context;
         mPm = pm;
-        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-
-        mStagingDir = stagingDir;
 
         mInstallThread = new HandlerThread(TAG);
         mInstallThread.start();
@@ -208,8 +217,9 @@
         synchronized (mSessions) {
             readSessionsLocked();
 
+            final File internalStagingDir = buildInternalStagingDir();
             final ArraySet<File> unclaimedStages = Sets.newArraySet(
-                    mStagingDir.listFiles(sStageFilter));
+                    internalStagingDir.listFiles(sStageFilter));
             final ArraySet<File> unclaimedIcons = Sets.newArraySet(
                     mSessionsDir.listFiles());
 
@@ -238,6 +248,11 @@
         }
     }
 
+    public void systemReady() {
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+        mStorage = mContext.getSystemService(StorageManager.class);
+    }
+
     public void onSecureContainersAvailable() {
         synchronized (mSessions) {
             final ArraySet<String> unclaimed = new ArraySet<>();
@@ -275,13 +290,13 @@
     }
 
     @Deprecated
-    public File allocateInternalStageDirLegacy() throws IOException {
+    public File allocateStageDirLegacy(String volumeUuid) throws IOException {
         synchronized (mSessions) {
             try {
                 final int sessionId = allocateSessionIdLocked();
                 mLegacySessions.put(sessionId, true);
-                final File stageDir = buildInternalStageDir(sessionId);
-                prepareInternalStageDir(stageDir);
+                final File stageDir = buildStageDir(volumeUuid, sessionId);
+                prepareStageDir(stageDir);
                 return stageDir;
             } catch (IllegalStateException e) {
                 throw new IOException(e);
@@ -322,11 +337,6 @@
                             Slog.w(TAG, "Abandoning old session first created at "
                                     + session.createdMillis);
                             valid = false;
-                        } else if (session.stageDir != null
-                                && !session.stageDir.exists()) {
-                            Slog.w(TAG, "Abandoning internal session with missing stage "
-                                    + session.stageDir);
-                            valid = false;
                         } else {
                             valid = true;
                         }
@@ -378,6 +388,7 @@
         params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
+        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
 
         final File appIconFile = buildAppIconFile(sessionId);
         if (appIconFile.exists()) {
@@ -448,6 +459,7 @@
         writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
         writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
         writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
+        writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
 
         // Persist app icon if changed since last written
         final File appIconFile = buildAppIconFile(session.sessionId);
@@ -528,28 +540,40 @@
             }
         }
 
-        if (params.mode == SessionParams.MODE_FULL_INSTALL
-                || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+        switch (params.mode) {
+            case SessionParams.MODE_FULL_INSTALL:
+            case SessionParams.MODE_INHERIT_EXISTING:
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid install mode: " + params.mode);
+        }
+
+        // If caller requested explicit location, sanity check it, otherwise
+        // resolve the best internal or adopted location.
+        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+            if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
+                throw new IOException("No suitable internal storage available");
+            }
+
+        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+            if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
+                throw new IOException("No suitable external storage available");
+            }
+
+        } else {
+            // For now, installs to adopted media are treated as internal from
+            // an install flag point-of-view.
+            params.setInstallFlagsInternal();
+
             // Resolve best location for install, based on combination of
             // requested install flags, delta size, and manifest settings.
             final long ident = Binder.clearCallingIdentity();
             try {
-                final int resolved = PackageHelper.resolveInstallLocation(mContext,
-                        params.appPackageName, params.installLocation, params.sizeBytes,
-                        params.installFlags);
-
-                if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
-                    params.setInstallFlagsInternal();
-                } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
-                    params.setInstallFlagsExternal();
-                } else {
-                    throw new IOException("No storage with enough free space; res=" + resolved);
-                }
+                params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
+                        params.appPackageName, params.installLocation, params.sizeBytes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-        } else {
-            throw new IllegalArgumentException("Invalid install mode: " + params.mode);
         }
 
         final int sessionId;
@@ -574,7 +598,7 @@
             File stageDir = null;
             String stageCid = null;
             if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
-                stageDir = buildInternalStageDir(sessionId);
+                stageDir = buildStageDir(params.volumeUuid, sessionId);
             } else {
                 stageCid = buildExternalStageCid(sessionId);
             }
@@ -673,11 +697,30 @@
         throw new IllegalStateException("Failed to allocate session ID");
     }
 
-    private File buildInternalStageDir(int sessionId) {
-        return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
+    private File buildInternalStagingDir() {
+        return new File(Environment.getDataDirectory(), "app");
     }
 
-    static void prepareInternalStageDir(File stageDir) throws IOException {
+    private File buildStagingDir(String volumeUuid) throws FileNotFoundException {
+        if (volumeUuid == null) {
+            return buildInternalStagingDir();
+        } else {
+            final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
+            if (vol != null && vol.type == VolumeInfo.TYPE_PRIVATE
+                    && vol.state == VolumeInfo.STATE_MOUNTED) {
+                return new File(vol.path, "app");
+            } else {
+                throw new FileNotFoundException("Failed to find volume for UUID " + volumeUuid);
+            }
+        }
+    }
+
+    private File buildStageDir(String volumeUuid, int sessionId) throws FileNotFoundException {
+        final File stagingDir = buildStagingDir(volumeUuid);
+        return new File(stagingDir, "vmdl" + sessionId + ".tmp");
+    }
+
+    static void prepareStageDir(File stageDir) throws IOException {
         if (stageDir.exists()) {
             throw new IOException("Session dir already exists: " + stageDir);
         }
@@ -749,16 +792,34 @@
     }
 
     @Override
-    public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
-        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall");
+    public void uninstall(String packageName, String callerPackageName, int flags,
+                IntentSender statusReceiver, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+        if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
+            mAppOps.checkPackage(callingUid, callerPackageName);
+        }
+
+        // Check whether the caller is device owner
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(callerPackageName);
 
         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
-                statusReceiver, packageName);
+                statusReceiver, packageName, isDeviceOwner, userId);
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                 == PackageManager.PERMISSION_GRANTED) {
             // Sweet, call straight through!
             mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
-
+        } else if (isDeviceOwner) {
+            // Allow the DeviceOwner to silently delete packages
+            // Need to clear the calling identity to get DELETE_PACKAGES permission
+            long ident = Binder.clearCallingIdentity();
+            try {
+                mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         } else {
             // Take a short detour to confirm with user
             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
@@ -814,12 +875,21 @@
         private final Context mContext;
         private final IntentSender mTarget;
         private final String mPackageName;
+        private final Notification mNotification;
 
         public PackageDeleteObserverAdapter(Context context, IntentSender target,
-                String packageName) {
+                String packageName, boolean showNotification, int userId) {
             mContext = context;
             mTarget = target;
             mPackageName = packageName;
+            if (showNotification) {
+                mNotification = buildSuccessNotification(mContext,
+                        mContext.getResources().getString(R.string.package_deleted_device_owner),
+                        packageName,
+                        userId);
+            } else {
+                mNotification = null;
+            }
         }
 
         @Override
@@ -837,6 +907,11 @@
 
         @Override
         public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+            if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
+                NotificationManager notificationManager = (NotificationManager)
+                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                notificationManager.notify(basePackageName, 0, mNotification);
+            }
             final Intent fillIn = new Intent();
             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
@@ -855,11 +930,16 @@
         private final Context mContext;
         private final IntentSender mTarget;
         private final int mSessionId;
+        private final boolean mShowNotification;
+        private final int mUserId;
 
-        public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) {
+        public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
+                boolean showNotification, int userId) {
             mContext = context;
             mTarget = target;
             mSessionId = sessionId;
+            mShowNotification = showNotification;
+            mUserId = userId;
         }
 
         @Override
@@ -878,6 +958,17 @@
         @Override
         public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                 Bundle extras) {
+            if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
+                Notification notification = buildSuccessNotification(mContext,
+                        mContext.getResources().getString(R.string.package_installed_device_owner),
+                        basePackageName,
+                        mUserId);
+                if (notification != null) {
+                    NotificationManager notificationManager = (NotificationManager)
+                            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+                    notificationManager.notify(basePackageName, 0, notification);
+                }
+            }
             final Intent fillIn = new Intent();
             fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
@@ -899,6 +990,40 @@
         }
     }
 
+    /**
+     * Build a notification for package installation / deletion by device owners that is shown if
+     * the operation succeeds.
+     */
+    private static Notification buildSuccessNotification(Context context, String contentText,
+            String basePackageName, int userId) {
+        PackageInfo packageInfo = null;
+        try {
+            packageInfo = AppGlobals.getPackageManager().getPackageInfo(
+                    basePackageName, 0, userId);
+        } catch (RemoteException ignored) {
+        }
+        if (packageInfo == null || packageInfo.applicationInfo == null) {
+            Slog.w(TAG, "Notification not built for package: " + basePackageName);
+            return null;
+        }
+        PackageManager pm = context.getPackageManager();
+        Bitmap packageIcon = ImageUtils.buildScaledBitmap(
+                packageInfo.applicationInfo.loadIcon(pm),
+                context.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_width),
+                context.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_height));
+        CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
+        return new Notification.Builder(context)
+                .setSmallIcon(R.drawable.ic_check_circle_24px)
+                .setColor(context.getResources().getColor(
+                        R.color.system_notification_accent_color))
+                .setContentTitle(packageLabel)
+                .setContentText(contentText)
+                .setLargeIcon(packageIcon)
+                .build();
+    }
+
     private static class Callbacks extends Handler {
         private static final int MSG_SESSION_CREATED = 1;
         private static final int MSG_SESSION_BADGING_CHANGED = 2;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index cc1b3ad..46db2d8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,8 +25,9 @@
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
 import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
-import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir;
+import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -61,6 +62,9 @@
 import android.util.MathUtils;
 import android.util.Slog;
 
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
@@ -69,9 +73,6 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -92,6 +93,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final Handler mHandler;
+    private final boolean mIsInstallerDeviceOwner;
 
     final int sessionId;
     final int userId;
@@ -208,8 +210,15 @@
         mPrepared = prepared;
         mSealed = sealed;
 
+        // Device owners are allowed to silently install packages, so the permission check is
+        // waived if the installer is the device owner.
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
         if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
-                == PackageManager.PERMISSION_GRANTED) || (installerUid == Process.ROOT_UID)) {
+                == PackageManager.PERMISSION_GRANTED)
+                || (installerUid == Process.ROOT_UID)
+                || mIsInstallerDeviceOwner) {
             mPermissionsAccepted = true;
         } else {
             mPermissionsAccepted = false;
@@ -440,7 +449,7 @@
         mActiveCount.incrementAndGet();
 
         final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
-                statusReceiver, sessionId);
+                statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
         mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
     }
 
@@ -892,7 +901,7 @@
         synchronized (mLock) {
             if (!mPrepared) {
                 if (stageDir != null) {
-                    prepareInternalStageDir(stageDir);
+                    prepareStageDir(stageDir);
                 } else if (stageCid != null) {
                     prepareExternalStageCid(stageCid, params.sizeBytes);
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2ff1718..e7ba582 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,11 +43,17 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
+import static android.content.pm.PackageManager.INSTALL_INTERNAL;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.MOVE_EXTERNAL_MEDIA;
+import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
+import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
+import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+import static android.content.pm.PackageManager.MOVE_INTERNAL;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
@@ -65,31 +71,7 @@
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 
 import android.Manifest;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.util.ArrayMap;
-
-import com.android.internal.R;
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.app.ResolverActivity;
-import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.PackageHelper;
-import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.EventLogTags;
-import com.android.server.IntentResolver;
-import com.android.server.LocalServices;
-import com.android.server.ServiceThread;
-import com.android.server.SystemConfig;
-import com.android.server.Watchdog;
-import com.android.server.pm.Settings.DatabaseVersion;
-import com.android.server.storage.DeviceStorageMonitorInternal;
-
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
@@ -119,6 +101,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
@@ -127,10 +110,10 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
+import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
 import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
@@ -150,13 +133,9 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
-import android.os.storage.IMountService;
-import android.os.storage.StorageEventListener;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.os.Debug;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -172,6 +151,10 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.IMountService;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.security.KeyStore;
 import android.security.SystemKeyStore;
 import android.system.ErrnoException;
@@ -179,6 +162,7 @@
 import android.system.StructStat;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.DisplayMetrics;
@@ -193,6 +177,33 @@
 import android.util.Xml;
 import android.view.Display;
 
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import com.android.internal.R;
+import com.android.internal.app.IMediaContainerService;
+import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.EventLogTags;
+import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
+import com.android.server.Watchdog;
+import com.android.server.pm.Settings.DatabaseVersion;
+import com.android.server.storage.DeviceStorageMonitorInternal;
+
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
@@ -227,12 +238,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-import dalvik.system.DexFile;
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
 /**
  * Keep track of all those .apks everywhere.
  * 
@@ -262,8 +267,7 @@
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
 
-    static final boolean RUNTIME_PERMISSIONS_ENABLED =
-            SystemProperties.getInt("ro.runtime.permissions.enabled", 0) == 1;
+    static final boolean RUNTIME_PERMISSIONS_ENABLED = true;
 
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
@@ -1539,9 +1543,14 @@
     private StorageEventListener mStorageListener = new StorageEventListener() {
         @Override
         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
-            Slog.v(TAG, vol.toString());
+            if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+                if (vol.state == VolumeInfo.STATE_MOUNTED) {
+                    loadPrivatePackages(vol);
+                } else if (vol.state == VolumeInfo.STATE_UNMOUNTING) {
+                    unloadPrivatePackages(vol);
+                }
+            }
 
-            // TODO: when private volume shows up, look for packages there too
             if (vol.isPrimary() && vol.type == VolumeInfo.TYPE_PUBLIC) {
                 if (vol.state == VolumeInfo.STATE_MOUNTED) {
                     updateExternalMediaStatus(true, false);
@@ -2160,7 +2169,7 @@
 
             mRequiredVerifierPackage = getRequiredVerifierLPr();
 
-            mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+            mInstallerService = new PackageInstallerService(context, this);
 
             mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
             mIntentFilterVerifier = new IntentVerifierProxy(mContext,
@@ -8476,8 +8485,8 @@
     public void installPackage(String originPath, IPackageInstallObserver2 observer,
             int installFlags, String installerPackageName, VerificationParams verificationParams,
             String packageAbiOverride) {
-        installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
-                packageAbiOverride, UserHandle.getCallingUserId());
+        installPackageAsUser(originPath, observer, installFlags, installerPackageName,
+                verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
     }
 
     @Override
@@ -8524,7 +8533,7 @@
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         msg.obj = new InstallParams(origin, observer, installFlags,
-                installerPackageName, verificationParams, user, packageAbiOverride);
+                installerPackageName, null, verificationParams, user, packageAbiOverride);
         mHandler.sendMessage(msg);
     }
 
@@ -8543,7 +8552,7 @@
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         msg.obj = new InstallParams(origin, observer, params.installFlags,
-                installerPackageName, verifParams, user, params.abiOverride);
+                installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride);
         mHandler.sendMessage(msg);
     }
 
@@ -9345,19 +9354,21 @@
         final IPackageInstallObserver2 observer;
         int installFlags;
         final String installerPackageName;
+        final String volumeUuid;
         final VerificationParams verificationParams;
         private InstallArgs mArgs;
         private int mRet;
         final String packageAbiOverride;
 
         InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
-                String installerPackageName, VerificationParams verificationParams, UserHandle user,
-                String packageAbiOverride) {
+                String installerPackageName, String volumeUuid,
+                VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
             super(user);
             this.origin = origin;
             this.observer = observer;
             this.installFlags = installFlags;
             this.installerPackageName = installerPackageName;
+            this.volumeUuid = volumeUuid;
             this.verificationParams = verificationParams;
             this.packageAbiOverride = packageAbiOverride;
         }
@@ -9711,7 +9722,7 @@
      * @param installFlags package installation flags
      * @return true if should be installed on external storage
      */
-    private static boolean installOnSd(int installFlags) {
+    private static boolean installOnExternalAsec(int installFlags) {
         if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
             return false;
         }
@@ -9732,7 +9743,7 @@
     }
 
     private InstallArgs createInstallArgs(InstallParams params) {
-        if (installOnSd(params.installFlags) || params.isForwardLocked()) {
+        if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
             return new AsecInstallArgs(params);
         } else {
             return new FileInstallArgs(params);
@@ -9746,7 +9757,7 @@
     private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
             String resourcePath, String nativeLibraryRoot, String[] instructionSets) {
         final boolean isInAsec;
-        if (installOnSd(installFlags)) {
+        if (installOnExternalAsec(installFlags)) {
             /* Apps on SD card are always in ASEC containers. */
             isInAsec = true;
         } else if (installForwardLocked(installFlags)
@@ -9762,7 +9773,7 @@
 
         if (isInAsec) {
             return new AsecInstallArgs(codePath, instructionSets,
-                    installOnSd(installFlags), installForwardLocked(installFlags));
+                    installOnExternalAsec(installFlags), installForwardLocked(installFlags));
         } else {
             return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot,
                     instructionSets);
@@ -9777,6 +9788,7 @@
         // Always refers to PackageManager flags only
         final int installFlags;
         final String installerPackageName;
+        final String volumeUuid;
         final ManifestDigest manifestDigest;
         final UserHandle user;
         final String abiOverride;
@@ -9787,12 +9799,13 @@
         /* nullable */ String[] instructionSets;
 
         InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags,
-                String installerPackageName, ManifestDigest manifestDigest, UserHandle user,
-                String[] instructionSets, String abiOverride) {
+                String installerPackageName, String volumeUuid, ManifestDigest manifestDigest,
+                UserHandle user, String[] instructionSets, String abiOverride) {
             this.origin = origin;
             this.installFlags = installFlags;
             this.observer = observer;
             this.installerPackageName = installerPackageName;
+            this.volumeUuid = volumeUuid;
             this.manifestDigest = manifestDigest;
             this.user = user;
             this.instructionSets = instructionSets;
@@ -9844,7 +9857,7 @@
             return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
         }
 
-        protected boolean isExternal() {
+        protected boolean isExternalAsec() {
             return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
         }
 
@@ -9891,8 +9904,8 @@
         /** New install */
         FileInstallArgs(InstallParams params) {
             super(params.origin, params.observer, params.installFlags,
-                    params.installerPackageName, params.getManifestDigest(), params.getUser(),
-                    null /* instruction sets */, params.packageAbiOverride);
+                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.getUser(), null /* instruction sets */, params.packageAbiOverride);
             if (isFwdLocked()) {
                 throw new IllegalArgumentException("Forward locking only supported in ASEC");
             }
@@ -9901,7 +9914,7 @@
         /** Existing install */
         FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
                 String[] instructionSets) {
-            super(OriginInfo.fromNothing(), null, 0, null, null, null, instructionSets, null);
+            super(OriginInfo.fromNothing(), null, 0, null, null, null, null, instructionSets, null);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
             this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
@@ -9925,7 +9938,7 @@
             }
 
             try {
-                final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
+                final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
                 codeFile = tempDir;
                 resourceFile = tempDir;
             } catch (IOException e) {
@@ -9986,8 +9999,9 @@
                 cleanUp();
                 return false;
             } else {
+                final File targetDir = codeFile.getParentFile();
                 final File beforeCodeFile = codeFile;
-                final File afterCodeFile = getNextCodePath(pkg.packageName);
+                final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
 
                 Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
                 try {
@@ -10137,16 +10151,15 @@
         /** New install */
         AsecInstallArgs(InstallParams params) {
             super(params.origin, params.observer, params.installFlags,
-                    params.installerPackageName, params.getManifestDigest(),
-                    params.getUser(), null /* instruction sets */,
-                    params.packageAbiOverride);
+                    params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
+                    params.getUser(), null /* instruction sets */, params.packageAbiOverride);
         }
 
         /** Existing install */
         AsecInstallArgs(String fullCodePath, String[] instructionSets,
                         boolean isExternal, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, (isExternal ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
                     instructionSets, null);
             // Hackily pretend we're still looking at a full code path
             if (!fullCodePath.endsWith(RES_FILE_NAME)) {
@@ -10163,7 +10176,7 @@
 
         AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null,
                     instructionSets, null);
             this.cid = cid;
             setMountPath(PackageHelper.getSdDir(cid));
@@ -10178,7 +10191,7 @@
                     abiOverride);
 
             final File target;
-            if (isExternal()) {
+            if (isExternalAsec()) {
                 target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory();
             } else {
                 target = Environment.getDataDirectory();
@@ -10207,7 +10220,7 @@
             }
 
             final String newMountPath = imcs.copyPackageToContainer(
-                    origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
+                    origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternalAsec(),
                     isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */));
 
             if (newMountPath != null) {
@@ -10488,11 +10501,11 @@
         return prefix + idxStr;
     }
 
-    private File getNextCodePath(String packageName) {
+    private File getNextCodePath(File targetDir, String packageName) {
         int suffix = 1;
         File result;
         do {
-            result = new File(mAppInstallDir, packageName + "-" + suffix);
+            result = new File(targetDir, packageName + "-" + suffix);
             suffix++;
         } while (result.exists());
         return result;
@@ -10556,9 +10569,9 @@
     /*
      * Install a non-existing package.
      */
-    private void installNewPackageLI(PackageParser.Package pkg,
-            int parseFlags, int scanFlags, UserHandle user,
-            String installerPackageName, PackageInstalledInfo res) {
+    private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
+            UserHandle user, String installerPackageName, String volumeUuid,
+            PackageInstalledInfo res) {
         // Remember this for later, in case we need to rollback this install
         String pkgName = pkg.packageName;
 
@@ -10587,7 +10600,7 @@
             PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
                     System.currentTimeMillis(), user);
 
-            updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
+            updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
             // delete the partially installed application. the data directory will have to be
             // restored if it was already existing
             if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
@@ -10619,9 +10632,9 @@
         return false;
     }
 
-    private void replacePackageLI(PackageParser.Package pkg,
-            int parseFlags, int scanFlags, UserHandle user,
-            String installerPackageName, PackageInstalledInfo res) {
+    private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
+            UserHandle user, String installerPackageName, String volumeUuid,
+            PackageInstalledInfo res) {
         PackageParser.Package oldPackage;
         String pkgName = pkg.packageName;
         int[] allUsers;
@@ -10660,17 +10673,17 @@
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, res);
+                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
         } else {
             replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, res);
+                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
-            int[] allUsers, boolean[] perUserInstalled,
-            String installerPackageName, PackageInstalledInfo res) {
+            int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
+            String volumeUuid, PackageInstalledInfo res) {
         String pkgName = deletedPackage.packageName;
         boolean deletedPkg = true;
         boolean updatedSettings = false;
@@ -10709,8 +10722,8 @@
             try {
                 final PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags,
                         scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
-                updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res,
-                        user);
+                updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+                        perUserInstalled, res, user);
                 updatedSettings = true;
             } catch (PackageManagerException e) {
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
@@ -10735,10 +10748,10 @@
                 if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
                 File restoreFile = new File(deletedPackage.codePath);
                 // Parse old package
-                boolean oldOnSd = isExternal(deletedPackage);
+                boolean oldExternal = isExternal(deletedPackage);
                 int oldParseFlags  = mDefParseFlags | PackageParser.PARSE_CHATTY |
                         (deletedPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0) |
-                        (oldOnSd ? PackageParser.PARSE_ON_SDCARD : 0);
+                        (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
                 int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                 try {
                     scanPackageLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);
@@ -10762,8 +10775,8 @@
 
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
-            int[] allUsers, boolean[] perUserInstalled,
-            String installerPackageName, PackageInstalledInfo res) {
+            int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
+            String volumeUuid, PackageInstalledInfo res) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                 + ", old=" + deletedPackage);
         boolean disabledSystem = false;
@@ -10840,8 +10853,8 @@
             }
 
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                updateSettingsLI(newPackage, installerPackageName, allUsers, perUserInstalled, res,
-                        user);
+                updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+                        perUserInstalled, res, user);
                 updatedSettings = true;
             }
 
@@ -10876,8 +10889,8 @@
     }
 
     private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
-            int[] allUsers, boolean[] perUserInstalled,
-            PackageInstalledInfo res, UserHandle user) {
+            String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
+            UserHandle user) {
         String pkgName = newPackage.packageName;
         synchronized (mPackages) {
             //write settings. the installStatus will be incomplete at this stage.
@@ -10935,6 +10948,7 @@
             res.pkg = newPackage;
             mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
             mSettings.setInstallerPackageName(pkgName, installerPackageName);
+            mSettings.setVolumeUuid(pkgName, volumeUuid);
             res.returnCode = PackageManager.INSTALL_SUCCEEDED;
             //to update install status
             mSettings.writeLPr();
@@ -10943,10 +10957,12 @@
 
     private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
         final int installFlags = args.installFlags;
-        String installerPackageName = args.installerPackageName;
-        File tmpPackageFile = new File(args.getCodePath());
-        boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
-        boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
+        final String installerPackageName = args.installerPackageName;
+        final String volumeUuid = args.volumeUuid;
+        final File tmpPackageFile = new File(args.getCodePath());
+        final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
+        final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
+                || (args.volumeUuid != null));
         boolean replace = false;
         final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
         // Result object to be returned
@@ -10956,7 +10972,7 @@
         // Retrieve PackageSettings and parse package
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
-                | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
+                | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setDisplayMetrics(mMetrics);
@@ -11108,7 +11124,7 @@
 
         }
 
-        if (systemApp && onSd) {
+        if (systemApp && onExternal) {
             // Disable updates to system apps on sdcard
             res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                     "Cannot install updates to system apps on sdcard");
@@ -11134,10 +11150,10 @@
         if (replace) {
             // Call replacePackageLI with SCAN_NO_DEX, since we already made dexopt
             replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING | SCAN_NO_DEX, args.user,
-                    installerPackageName, res);
+                    installerPackageName, volumeUuid, res);
         } else {
             installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
-                    args.user, installerPackageName, res);
+                    args.user, installerPackageName, volumeUuid, res);
         }
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -11265,7 +11281,9 @@
 
     private int packageFlagsToInstallFlags(PackageSetting ps) {
         int installFlags = 0;
-        if (isExternal(ps)) {
+        if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
+            // This existing package was an external ASEC install when we have
+            // the external flag without a UUID
             installFlags |= PackageManager.INSTALL_EXTERNAL;
         }
         if (ps.isForwardLocked()) {
@@ -12975,6 +12993,8 @@
         // Watch for external volumes that come and go over time
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         storage.registerListener(mStorageListener);
+
+        mInstallerService.systemReady();
     }
 
     @Override
@@ -13713,13 +13733,32 @@
     }
 
     private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+            ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
+        final int size = infos.size();
+        final String[] packageNames = new String[size];
+        final int[] packageUids = new int[size];
+        for (int i = 0; i < size; i++) {
+            final ApplicationInfo info = infos.get(i);
+            packageNames[i] = info.packageName;
+            packageUids[i] = info.uid;
+        }
+        sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
+                finishedReceiver);
+    }
+
+    private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
             ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
-        int size = pkgList.size();
+        sendResourcesChangedBroadcast(mediaStatus, replacing,
+                pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
+    }
+
+    private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+            String[] pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+        int size = pkgList.length;
         if (size > 0) {
             // Send broadcasts here
             Bundle extras = new Bundle();
-            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList
-                    .toArray(new String[size]));
+            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
             if (uidArr != null) {
                 extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
             }
@@ -13762,8 +13801,8 @@
                 }
                 // Parse package
                 int parseFlags = mDefParseFlags;
-                if (args.isExternal()) {
-                    parseFlags |= PackageParser.PARSE_ON_SDCARD;
+                if (args.isExternalAsec()) {
+                    parseFlags |= PackageParser.PARSE_EXTERNAL_STORAGE;
                 }
                 if (args.isFwdLocked()) {
                     parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
@@ -13910,73 +13949,130 @@
         }
     }
 
-    /** Binder call */
+    private void loadPrivatePackages(VolumeInfo vol) {
+        final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
+        final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
+        synchronized (mPackages) {
+            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+            for (PackageSetting ps : packages) {
+                synchronized (mInstallLock) {
+                    final PackageParser.Package pkg;
+                    try {
+                        pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null);
+                        loaded.add(pkg.applicationInfo);
+                    } catch (PackageManagerException e) {
+                        Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
+                    }
+                }
+            }
+
+            // TODO: regrant any permissions that changed based since original install
+
+            mSettings.writeLPr();
+        }
+
+        Slog.d(TAG, "Loaded packages " + loaded);
+        sendResourcesChangedBroadcast(true, false, loaded, null);
+    }
+
+    private void unloadPrivatePackages(VolumeInfo vol) {
+        final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
+        synchronized (mPackages) {
+            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid);
+            for (PackageSetting ps : packages) {
+                if (ps.pkg == null) continue;
+                synchronized (mInstallLock) {
+                    final ApplicationInfo info = ps.pkg.applicationInfo;
+                    final PackageRemovedInfo outInfo = new PackageRemovedInfo();
+                    if (deletePackageLI(ps.name, null, false, null, null,
+                            PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
+                        unloaded.add(info);
+                    } else {
+                        Slog.w(TAG, "Failed to unload " + ps.codePath);
+                    }
+                }
+            }
+
+            mSettings.writeLPr();
+        }
+
+        Slog.d(TAG, "Unloaded packages " + unloaded);
+        sendResourcesChangedBroadcast(false, false, unloaded, null);
+    }
+
     @Override
     public void movePackage(final String packageName, final IPackageMoveObserver observer,
             final int flags) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
-        UserHandle user = new UserHandle(UserHandle.getCallingUserId());
-        int returnCode = PackageManager.MOVE_SUCCEEDED;
-        int currInstallFlags = 0;
-        int newInstallFlags = 0;
+
+        final int installFlags;
+        if ((flags & MOVE_INTERNAL) != 0) {
+            installFlags = INSTALL_INTERNAL;
+        } else if ((flags & MOVE_EXTERNAL_MEDIA) != 0) {
+            installFlags = INSTALL_EXTERNAL;
+        } else {
+            throw new IllegalArgumentException("Unsupported move flags " + flags);
+        }
+
+        try {
+            movePackageInternal(packageName, null, installFlags, false, observer);
+        } catch (PackageManagerException e) {
+            Slog.d(TAG, "Failed to move " + packageName, e);
+            try {
+                observer.packageMoved(packageName, e.error);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    @Override
+    public void movePackageAndData(final String packageName, final String volumeUuid,
+            final IPackageMoveObserver observer) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
+        try {
+            movePackageInternal(packageName, volumeUuid, INSTALL_INTERNAL, true, observer);
+        } catch (PackageManagerException e) {
+            Slog.d(TAG, "Failed to move " + packageName, e);
+            try {
+                observer.packageMoved(packageName, e.error);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void movePackageInternal(final String packageName, String volumeUuid, int installFlags,
+            boolean andData, final IPackageMoveObserver observer) throws PackageManagerException {
+        final UserHandle user = new UserHandle(UserHandle.getCallingUserId());
 
         File codeFile = null;
         String installerPackageName = null;
         String packageAbiOverride = null;
 
+        // TOOD: move app private data before installing
+
         // reader
         synchronized (mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (pkg == null || ps == null) {
-                returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
-            } else {
-                // Disable moving fwd locked apps and system packages
-                if (pkg.applicationInfo != null && isSystemApp(pkg)) {
-                    Slog.w(TAG, "Cannot move system application");
-                    returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
-                } else if (pkg.mOperationPending) {
-                    Slog.w(TAG, "Attempt to move package which has pending operations");
-                    returnCode = PackageManager.MOVE_FAILED_OPERATION_PENDING;
-                } else {
-                    // Find install location first
-                    if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
-                            && (flags & PackageManager.MOVE_INTERNAL) != 0) {
-                        Slog.w(TAG, "Ambigous flags specified for move location.");
-                        returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-                    } else {
-                        newInstallFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0
-                                ? PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
-                        currInstallFlags = isExternal(pkg)
-                                ? PackageManager.INSTALL_EXTERNAL : PackageManager.INSTALL_INTERNAL;
-
-                        if (newInstallFlags == currInstallFlags) {
-                            Slog.w(TAG, "No move required. Trying to move to same location");
-                            returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-                        } else {
-                            if (pkg.isForwardLocked()) {
-                                currInstallFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-                                newInstallFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-                            }
-                        }
-                    }
-                    if (returnCode == PackageManager.MOVE_SUCCEEDED) {
-                        pkg.mOperationPending = true;
-                    }
-                }
-
-                codeFile = new File(pkg.codePath);
-                installerPackageName = ps.installerPackageName;
-                packageAbiOverride = ps.cpuAbiOverrideString;
+                throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
             }
-        }
 
-        if (returnCode != PackageManager.MOVE_SUCCEEDED) {
-            try {
-                observer.packageMoved(packageName, returnCode);
-            } catch (RemoteException ignored) {
+            if (pkg.applicationInfo.isSystemApp()) {
+                throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
+                        "Cannot move system application");
+            } else if (pkg.mOperationPending) {
+                throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
+                        "Attempt to move package which has pending operations");
             }
-            return;
+
+            // TODO: yell if already in desired location
+
+            pkg.mOperationPending = true;
+
+            codeFile = new File(pkg.codePath);
+            installerPackageName = ps.installerPackageName;
+            packageAbiOverride = ps.cpuAbiOverrideString;
         }
 
         final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@@ -14018,12 +14114,12 @@
 
         // Treat a move like reinstalling an existing app, which ensures that we
         // process everythign uniformly, like unpacking native libraries.
-        newInstallFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+        installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
 
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
-        msg.obj = new InstallParams(origin, installObserver, newInstallFlags,
-                installerPackageName, null, user, packageAbiOverride);
+        msg.obj = new InstallParams(origin, installObserver, installFlags,
+                installerPackageName, volumeUuid, null, user, packageAbiOverride);
         mHandler.sendMessage(msg);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index daa6d64..f294b32 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -23,6 +23,7 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
+import android.os.storage.VolumeInfo;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -108,8 +109,10 @@
 
     PackageSettingBase origPackage;
 
-    /* package name of the app that installed this package */
+    /** Package name of the app that installed this package */
     String installerPackageName;
+    /** UUID of {@link VolumeInfo} hosting this app */
+    String volumeUuid;
 
     IntentFilterVerificationInfo verificationInfo;
 
@@ -161,6 +164,7 @@
         origPackage = base.origPackage;
 
         installerPackageName = base.installerPackageName;
+        volumeUuid = base.volumeUuid;
 
         keySetData = new PackageKeySetData(base.keySetData);
     }
@@ -183,10 +187,18 @@
         installerPackageName = packageName;
     }
 
-    String getInstallerPackageName() {
+    public String getInstallerPackageName() {
         return installerPackageName;
     }
 
+    public void setVolumeUuid(String volumeUuid) {
+        this.volumeUuid = volumeUuid;
+    }
+
+    public String getVolumeUuid() {
+        return volumeUuid;
+    }
+
     public void setInstallStatus(int newStatus) {
         installStatus = newStatus;
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6930965..6b7c35c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -42,6 +42,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.VolumeInfo;
 import android.util.AtomicFile;
 import android.text.TextUtils;
 import android.util.LogPrinter;
@@ -53,6 +54,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.PackageManagerService.DumpState;
@@ -333,14 +335,20 @@
         }
     }
 
-    void setInstallerPackageName(String pkgName,
-            String installerPkgName) {
+    void setInstallerPackageName(String pkgName, String installerPkgName) {
         PackageSetting p = mPackages.get(pkgName);
-        if(p != null) {
+        if (p != null) {
             p.setInstallerPackageName(installerPkgName);
         }
     }
 
+    void setVolumeUuid(String pkgName, String volumeUuid) {
+        PackageSetting p = mPackages.get(pkgName);
+        if (p != null) {
+            p.setVolumeUuid(volumeUuid);
+        }
+    }
+
     SharedUserSetting getSharedUserLPw(String name,
             int pkgFlags, int pkgPrivateFlags, boolean create) {
         SharedUserSetting s = mSharedUsers.get(name);
@@ -2066,6 +2074,9 @@
         if (pkg.installerPackageName != null) {
             serializer.attribute(null, "installer", pkg.installerPackageName);
         }
+        if (pkg.volumeUuid != null) {
+            serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
+        }
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
         if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
             writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
@@ -2908,6 +2919,7 @@
         String cpuAbiOverrideString = null;
         String systemStr = null;
         String installerPackageName = null;
+        String volumeUuid = null;
         String uidError = null;
         int pkgFlags = 0;
         int pkgPrivateFlags = 0;
@@ -2945,6 +2957,7 @@
                 }
             }
             installerPackageName = parser.getAttributeValue(null, "installer");
+            volumeUuid = parser.getAttributeValue(null, "volumeUuid");
 
             systemStr = parser.getAttributeValue(null, "publicFlags");
             if (systemStr != null) {
@@ -3093,6 +3106,7 @@
         if (packageSetting != null) {
             packageSetting.uidError = "true".equals(uidError);
             packageSetting.installerPackageName = installerPackageName;
+            packageSetting.volumeUuid = volumeUuid;
             packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
             packageSetting.primaryCpuAbiString = primaryCpuAbiString;
             packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -3549,6 +3563,22 @@
         return null;
     }
 
+    /**
+     * Return all {@link PackageSetting} that are actively installed on the
+     * given {@link VolumeInfo#fsUuid}.
+     */
+    List<PackageSetting> getVolumePackagesLPr(String volumeUuid) {
+        Preconditions.checkNotNull(volumeUuid);
+        ArrayList<PackageSetting> res = new ArrayList<>();
+        for (int i = 0; i < mPackages.size(); i++) {
+            final PackageSetting setting = mPackages.valueAt(i);
+            if (Objects.equals(volumeUuid, setting.volumeUuid)) {
+                res.add(setting);
+            }
+        }
+        return res;
+    }
+
     static void printFlags(PrintWriter pw, int val, Object[] spec) {
         pw.print("[ ");
         for (int i=0; i<spec.length; i+=2) {
@@ -3755,6 +3785,10 @@
             pw.print(prefix); pw.print("  installerPackageName=");
                     pw.println(ps.installerPackageName);
         }
+        if (ps.volumeUuid != null) {
+            pw.print(prefix); pw.print("  volumeUuid=");
+                    pw.println(ps.volumeUuid);
+        }
         pw.print(prefix); pw.print("  signatures="); pw.println(ps.signatures);
         pw.print(prefix); pw.print("  installPermissionsFixed=");
                 pw.print(ps.installPermissionsFixed);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0188e98..7fab5a6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9889,7 +9889,7 @@
                             }
                         }
 
-                        winAnimator.prepareSurfaceLocked(recoveringMemory);
+                        winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
 
                         final AppWindowToken atoken = w.mAppToken;
                         if (DEBUG_STARTING_WINDOW && atoken != null
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 056267d..ac1b0f1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1341,7 +1341,7 @@
         }
     }
 
-    private void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
+    void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
 
         int width;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 45584cd..bb3085e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -280,15 +280,12 @@
         long mLastMaximumTimeToLock = -1;
         boolean mUserSetupComplete = false;
 
-        final HashMap<ComponentName, ActiveAdmin> mAdminMap
-                = new HashMap<ComponentName, ActiveAdmin>();
-        final ArrayList<ActiveAdmin> mAdminList
-                = new ArrayList<ActiveAdmin>();
-        final ArrayList<ComponentName> mRemovingAdmins
-                = new ArrayList<ComponentName>();
+        final HashMap<ComponentName, ActiveAdmin> mAdminMap = new HashMap<>();
+        final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
+        final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
 
         // This is the list of component allowed to start lock task mode.
-        final List<String> mLockTaskPackages = new ArrayList<String>();
+        final List<String> mLockTaskPackages = new ArrayList<>();
 
         ComponentName mRestrictionsProvider;
 
@@ -299,7 +296,7 @@
         }
     }
 
-    final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>();
+    final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
 
     Handler mHandler = new Handler();
 
@@ -1596,6 +1593,16 @@
         validatePasswordOwnerLocked(policy);
         syncDeviceCapabilitiesLocked(policy);
         updateMaximumTimeToLockLocked(policy);
+        updateLockTaskPackagesLocked(policy, userHandle);
+    }
+
+    private void updateLockTaskPackagesLocked(DevicePolicyData policy, int userId) {
+        IActivityManager am = ActivityManagerNative.getDefault();
+        try {
+            am.updateLockTaskPackages(userId, policy.mLockTaskPackages.toArray(new String[0]));
+        } catch (RemoteException e) {
+            // Not gonna happen.
+        }
     }
 
     static void validateQualityConstant(int quality) {
@@ -5533,6 +5540,7 @@
 
             // Store the settings persistently.
             saveSettingsLocked(userHandle);
+            updateLockTaskPackagesLocked(policy, userHandle);
         }
     }
 
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index a204376..c8b6846 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -485,14 +485,19 @@
         throw new UnsupportedOperationException();
     }
 
-    /**
-     * @hide - to match hiding in superclass
-     */
+    /** {@hide} */
     @Override
     public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @Override
+    public void movePackageAndData(String packageName, String volumeUuid,
+            IPackageMoveObserver observer) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public String getInstallerPackageName(String packageName) {
         throw new UnsupportedOperationException();
diff --git a/tests/CameraPrewarmTest/Android.mk b/tests/CameraPrewarmTest/Android.mk
new file mode 100644
index 0000000..b6316f0
--- /dev/null
+++ b/tests/CameraPrewarmTest/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CameraPrewarmTest
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CameraPrewarmTest/AndroidManifest.xml b/tests/CameraPrewarmTest/AndroidManifest.xml
new file mode 100644
index 0000000..eb40200
--- /dev/null
+++ b/tests/CameraPrewarmTest/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.google.android.test.cameraprewarm">
+    <application android:label="@string/activity_title">
+
+        <activity android:name=".CameraActivity"
+                android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".SecureCameraActivity"
+                android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <receiver android:name=".PrewarmReceiver" >
+            <intent-filter>
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA_PREWARM" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.media.action.STILL_IMAGE_CAMERA_COOLDOWN" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+</manifest>
diff --git a/tests/CameraPrewarmTest/res/layout/camera_activity.xml b/tests/CameraPrewarmTest/res/layout/camera_activity.xml
new file mode 100644
index 0000000..64437bc
--- /dev/null
+++ b/tests/CameraPrewarmTest/res/layout/camera_activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center">
+</LinearLayout>
+
diff --git a/tests/CameraPrewarmTest/res/values/strings.xml b/tests/CameraPrewarmTest/res/values/strings.xml
new file mode 100644
index 0000000..11f7ac7
--- /dev/null
+++ b/tests/CameraPrewarmTest/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <string name="activity_title">Assistant</string>
+    <string name="search_label">Orilla Search Engine</string>
+</resources>
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
new file mode 100644
index 0000000..4d22234
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.google.android.test.cameraprewarm.R;
+
+public class CameraActivity extends Activity {
+
+    public final static String TAG = "PrewarmTest";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.camera_activity);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+        Log.i(TAG, "Activity created");
+    }
+}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java
new file mode 100644
index 0000000..d49f96d
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.MediaStore;
+import android.util.Log;
+
+public class PrewarmReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM)) {
+            Log.i(CameraActivity.TAG, "Prewarm received");
+        } else if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN)){
+            Log.i(CameraActivity.TAG, "Cooldown received");
+        }
+    }
+}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
new file mode 100644
index 0000000..530fe00
--- /dev/null
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.cameraprewarm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.google.android.test.cameraprewarm.R;
+
+public class SecureCameraActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.camera_activity);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        Log.i(CameraActivity.TAG, "Activity created");
+    }
+}