Merge "Changed list metered-networks so it returns all networks." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 6406f57..eb8befa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -104,6 +104,7 @@
     field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+    field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
@@ -4531,10 +4532,12 @@
     method public void noteStateNotSaved();
     method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
     method public void reportLoaderStart();
-    method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
     method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
     method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
-    method public java.util.List<android.app.Fragment> retainNonConfig();
+    method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
     method public android.os.Parcelable saveAllState();
   }
 
@@ -4594,6 +4597,9 @@
     method public abstract void onBackStackChanged();
   }
 
+  public class FragmentManagerNonConfig {
+  }
+
   public abstract class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
@@ -4604,6 +4610,8 @@
     method public abstract android.app.FragmentTransaction attach(android.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
     method public abstract android.app.FragmentTransaction detach(android.app.Fragment);
     method public abstract android.app.FragmentTransaction disallowAddToBackStack();
     method public abstract android.app.FragmentTransaction hide(android.app.Fragment);
diff --git a/api/system-current.txt b/api/system-current.txt
index ed094f9..bd9ca9c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4664,10 +4664,12 @@
     method public void noteStateNotSaved();
     method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
     method public void reportLoaderStart();
-    method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
     method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
     method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
-    method public java.util.List<android.app.Fragment> retainNonConfig();
+    method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
     method public android.os.Parcelable saveAllState();
   }
 
@@ -4727,6 +4729,9 @@
     method public abstract void onBackStackChanged();
   }
 
+  public class FragmentManagerNonConfig {
+  }
+
   public abstract class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
@@ -4737,6 +4742,8 @@
     method public abstract android.app.FragmentTransaction attach(android.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
     method public abstract android.app.FragmentTransaction detach(android.app.Fragment);
     method public abstract android.app.FragmentTransaction disallowAddToBackStack();
     method public abstract android.app.FragmentTransaction hide(android.app.Fragment);
@@ -21075,6 +21082,7 @@
   public final class AudioAttributes implements android.os.Parcelable {
     method public int describeContents();
     method public int getAllFlags();
+    method public android.os.Bundle getBundle();
     method public int getCapturePreset();
     method public int getContentType();
     method public int getFlags();
@@ -21113,6 +21121,7 @@
   public static class AudioAttributes.Builder {
     ctor public AudioAttributes.Builder();
     ctor public AudioAttributes.Builder(android.media.AudioAttributes);
+    method public android.media.AudioAttributes.Builder addBundle(android.os.Bundle);
     method public android.media.AudioAttributes build();
     method public android.media.AudioAttributes.Builder setCapturePreset(int);
     method public android.media.AudioAttributes.Builder setContentType(int);
@@ -31495,6 +31504,51 @@
     ctor public TransactionTooLargeException(java.lang.String);
   }
 
+  public class UpdateEngine {
+    ctor public UpdateEngine();
+    method public void applyPayload(java.lang.String, long, long, java.lang.String[]) throws android.os.RemoteException;
+    method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler) throws android.os.RemoteException;
+    method public boolean bind(android.os.UpdateEngineCallback) throws android.os.RemoteException;
+    method public void cancel() throws android.os.RemoteException;
+    method public void resume() throws android.os.RemoteException;
+    method public void suspend() throws android.os.RemoteException;
+  }
+
+  public static final class UpdateEngine.ErrorCodeConstants {
+    ctor public UpdateEngine.ErrorCodeConstants();
+    field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc
+    field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9
+    field public static final int ERROR = 1; // 0x1
+    field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4
+    field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7
+    field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8
+    field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
+    field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
+    field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+    field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
+    field public static final int SUCCESS = 0; // 0x0
+  }
+
+  public static final class UpdateEngine.UpdateStatusConstants {
+    ctor public UpdateEngine.UpdateStatusConstants();
+    field public static final int ATTEMPTING_ROLLBACK = 8; // 0x8
+    field public static final int CHECKING_FOR_UPDATE = 1; // 0x1
+    field public static final int DISABLED = 9; // 0x9
+    field public static final int DOWNLOADING = 3; // 0x3
+    field public static final int FINALIZING = 5; // 0x5
+    field public static final int IDLE = 0; // 0x0
+    field public static final int REPORTING_ERROR_EVENT = 7; // 0x7
+    field public static final int UPDATED_NEED_REBOOT = 6; // 0x6
+    field public static final int UPDATE_AVAILABLE = 2; // 0x2
+    field public static final int VERIFYING = 4; // 0x4
+  }
+
+  public abstract class UpdateEngineCallback {
+    ctor public UpdateEngineCallback();
+    method public abstract void onPayloadApplicationComplete(int);
+    method public abstract void onStatusUpdate(int, float);
+  }
+
   public final class UserHandle implements android.os.Parcelable {
     ctor public UserHandle(android.os.Parcel);
     method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 417107d..e7f4336 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -104,6 +104,7 @@
     field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+    field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
     field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
@@ -4531,10 +4532,12 @@
     method public void noteStateNotSaved();
     method public android.view.View onCreateView(android.view.View, java.lang.String, android.content.Context, android.util.AttributeSet);
     method public void reportLoaderStart();
-    method public void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public deprecated void restoreAllState(android.os.Parcelable, java.util.List<android.app.Fragment>);
+    method public void restoreAllState(android.os.Parcelable, android.app.FragmentManagerNonConfig);
     method public void restoreLoaderNonConfig(android.util.ArrayMap<java.lang.String, android.app.LoaderManager>);
     method public android.util.ArrayMap<java.lang.String, android.app.LoaderManager> retainLoaderNonConfig();
-    method public java.util.List<android.app.Fragment> retainNonConfig();
+    method public android.app.FragmentManagerNonConfig retainNestedNonConfig();
+    method public deprecated java.util.List<android.app.Fragment> retainNonConfig();
     method public android.os.Parcelable saveAllState();
   }
 
@@ -4594,6 +4597,9 @@
     method public abstract void onBackStackChanged();
   }
 
+  public class FragmentManagerNonConfig {
+  }
+
   public abstract class FragmentTransaction {
     ctor public FragmentTransaction();
     method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
@@ -4604,6 +4610,8 @@
     method public abstract android.app.FragmentTransaction attach(android.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
+    method public abstract void commitNow();
+    method public abstract void commitNowAllowingStateLoss();
     method public abstract android.app.FragmentTransaction detach(android.app.Fragment);
     method public abstract android.app.FragmentTransaction disallowAddToBackStack();
     method public abstract android.app.FragmentTransaction hide(android.app.Fragment);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index acc68cf..6206323 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1126,14 +1126,19 @@
         }
     }
 
+    private byte[] argToBytes(String arg) {
+        if (arg.equals("!")) {
+            return null;
+        } else {
+            return HexDump.hexStringToByteArray(arg);
+        }
+    }
+
     private void runUnlockUser() throws Exception {
         int userId = Integer.parseInt(nextArgRequired());
-        String tokenHex = nextArg();
-        byte[] token = null;
-        if (tokenHex != null) {
-            token = HexDump.hexStringToByteArray(tokenHex);
-        }
-        boolean success = mAm.unlockUser(userId, token);
+        byte[] token = argToBytes(nextArgRequired());
+        byte[] secret = argToBytes(nextArgRequired());
+        boolean success = mAm.unlockUser(userId, token, secret);
         if (success) {
             System.out.println("Success: user unlocked");
         } else {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e0eb389..622012e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -753,7 +753,7 @@
     static final class NonConfigurationInstances {
         Object activity;
         HashMap<String, Object> children;
-        List<Fragment> fragments;
+        FragmentManagerNonConfig fragments;
         ArrayMap<String, LoaderManager> loaders;
         VoiceInteractor voiceInteractor;
     }
@@ -2070,7 +2070,7 @@
     NonConfigurationInstances retainNonConfigurationInstances() {
         Object activity = onRetainNonConfigurationInstance();
         HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
-        List<Fragment> fragments = mFragments.retainNonConfig();
+        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
         ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
         if (activity == null && children == null && fragments == null && loaders == null
                 && mVoiceInteractor == null) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3160f4..1954774 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2080,7 +2080,8 @@
             data.enforceInterface(IActivityManager.descriptor);
             int userId = data.readInt();
             byte[] token = data.createByteArray();
-            boolean result = unlockUser(userId, token);
+            byte[] secret = data.createByteArray();
+            boolean result = unlockUser(userId, token, secret);
             reply.writeNoException();
             reply.writeInt(result ? 1 : 0);
             return true;
@@ -5571,12 +5572,13 @@
         return result;
     }
 
-    public boolean unlockUser(int userId, byte[] token) throws RemoteException {
+    public boolean unlockUser(int userId, byte[] token, byte[] secret) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeInt(userId);
         data.writeByteArray(token);
+        data.writeByteArray(secret);
         mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6424520..04883a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5021,6 +5021,9 @@
 
         final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
         if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
+            // This cache location probably points at credential-encrypted
+            // storage which may not be accessible yet; assign it anyway instead
+            // of pointing at device-encrypted storage.
             final File cacheDir = appContext.getCacheDir();
             if (cacheDir != null) {
                 // Provide a usable directory for temporary files
@@ -5030,8 +5033,12 @@
                         + "due to missing cache directory");
             }
 
-            // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
-            final File codeCacheDir = appContext.getCodeCacheDir();
+            // Setup a location to store generated/compiled graphics code and
+            // JIT profiling data. Note that this data is stored in a
+            // device-encrypted storage area, so these caches must never contain
+            // user sensitive user data.
+            final Context deviceContext = appContext.createDeviceEncryptedStorageContext();
+            final File codeCacheDir = deviceContext.getCodeCacheDir();
             if (codeCacheDir != null) {
                 setupGraphicsSupport(data.info, codeCacheDir);
                 setupJitProfileSupport(data.info, codeCacheDir);
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index a147cc8..eb4b13e 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -667,6 +667,18 @@
         return commitInternal(true);
     }
 
+    @Override
+    public void commitNow() {
+        disallowAddToBackStack();
+        mManager.execSingleAction(this, false);
+    }
+
+    @Override
+    public void commitNowAllowingStateLoss() {
+        disallowAddToBackStack();
+        mManager.execSingleAction(this, true);
+    }
+
     int commitInternal(boolean allowStateLoss) {
         if (mCommitted) {
             throw new IllegalStateException("commit already called");
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89d4931..eec503b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -58,6 +58,9 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.storage.IMountService;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -482,21 +485,20 @@
         return f.delete();
     }
 
-    // Common-path handling of app data dir creation
+    /**
+     * Common-path handling of app data dir creation
+     */
     private static File ensurePrivateDirExists(File file) {
         if (!file.exists()) {
-            if (!file.mkdirs()) {
-                if (file.exists()) {
-                    // spurious failure; probably racing with another process for this app
-                    return file;
+            try {
+                Os.mkdir(file.getAbsolutePath(), 0771);
+            } catch (ErrnoException e) {
+                if (e.errno == OsConstants.EEXIST) {
+                    // We must have raced with someone; that's okay
+                } else {
+                    Log.w(TAG, "Failed to ensure " + file + ": " + e.getMessage());
                 }
-                Log.w(TAG, "Failed to ensure directory " + file.getAbsolutePath());
-                return null;
             }
-            FileUtils.setPermissions(
-                    file.getPath(),
-                    FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-                    -1, -1);
         }
         return file;
     }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 688c9f8..02a898b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -97,35 +97,34 @@
         mSavedFragmentState = in.readBundle();
     }
 
-    public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
-        if (mInstance != null) {
-            return mInstance;
+    public Fragment instantiate(FragmentHostCallback host, Fragment parent,
+            FragmentManagerNonConfig childNonConfig) {
+        if (mInstance == null) {
+            final Context context = host.getContext();
+            if (mArguments != null) {
+                mArguments.setClassLoader(context.getClassLoader());
+            }
+
+            mInstance = Fragment.instantiate(context, mClassName, mArguments);
+
+            if (mSavedFragmentState != null) {
+                mSavedFragmentState.setClassLoader(context.getClassLoader());
+                mInstance.mSavedFragmentState = mSavedFragmentState;
+            }
+            mInstance.setIndex(mIndex, parent);
+            mInstance.mFromLayout = mFromLayout;
+            mInstance.mRestored = true;
+            mInstance.mFragmentId = mFragmentId;
+            mInstance.mContainerId = mContainerId;
+            mInstance.mTag = mTag;
+            mInstance.mRetainInstance = mRetainInstance;
+            mInstance.mDetached = mDetached;
+            mInstance.mHidden = mHidden;
+            mInstance.mFragmentManager = host.mFragmentManager;
+            if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+                    "Instantiated fragment " + mInstance);
         }
-
-        final Context context = host.getContext();
-        if (mArguments != null) {
-            mArguments.setClassLoader(context.getClassLoader());
-        }
-
-        mInstance = Fragment.instantiate(context, mClassName, mArguments);
-
-        if (mSavedFragmentState != null) {
-            mSavedFragmentState.setClassLoader(context.getClassLoader());
-            mInstance.mSavedFragmentState = mSavedFragmentState;
-        }
-        mInstance.setIndex(mIndex, parent);
-        mInstance.mFromLayout = mFromLayout;
-        mInstance.mRestored = true;
-        mInstance.mFragmentId = mFragmentId;
-        mInstance.mContainerId = mContainerId;
-        mInstance.mTag = mTag;
-        mInstance.mRetainInstance = mRetainInstance;
-        mInstance.mDetached = mDetached;
-        mInstance.mHidden = mHidden;
-        mInstance.mFragmentManager = host.mFragmentManager;
-        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
-                "Instantiated fragment " + mInstance);
-
+        mInstance.mChildNonConfig = childNonConfig;
         return mInstance;
     }
 
@@ -433,6 +432,10 @@
     // Private fragment manager for child fragments inside of this one.
     FragmentManagerImpl mChildFragmentManager;
 
+    // For use when restoring fragment state and descendant fragments are retained.
+    // This state is set by FragmentState.instantiate and cleared in onCreate.
+    FragmentManagerNonConfig mChildNonConfig;
+
     // If this Fragment is contained in another Fragment, this is that container.
     Fragment mParentFragment;
 
@@ -975,10 +978,6 @@
      * </ul>
      */
     public void setRetainInstance(boolean retain) {
-        if (retain && mParentFragment != null) {
-            throw new IllegalStateException(
-                    "Can't retain fragements that are nested in other fragments");
-        }
         mRetainInstance = retain;
     }
 
@@ -1436,7 +1435,8 @@
                     if (mChildFragmentManager == null) {
                         instantiateChildFragmentManager();
                     }
-                    mChildFragmentManager.restoreAllState(p, null);
+                    mChildFragmentManager.restoreAllState(p, mChildNonConfig);
+                    mChildNonConfig = null;
                     mChildFragmentManager.dispatchCreate();
                 }
             }
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index a9270bc4..57b0ff1 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -117,16 +117,40 @@
      * instances retained across configuration changes.
      *
      * @see #retainNonConfig()
+     *
+     * @deprecated use {@link #restoreAllState(Parcelable, FragmentManagerNonConfig)}
      */
     public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
-        mHost.mFragmentManager.restoreAllState(state, nonConfigList);
+        mHost.mFragmentManager.restoreAllState(state,
+                new FragmentManagerNonConfig(nonConfigList, null));
+    }
+
+    /**
+     * Restores the saved state for all Fragments. The given FragmentManagerNonConfig are Fragment
+     * instances retained across configuration changes, including nested fragments
+     *
+     * @see #retainNestedNonConfig()
+     */
+    public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
+        mHost.mFragmentManager.restoreAllState(state, nonConfig);
     }
 
     /**
      * Returns a list of Fragments that have opted to retain their instance across
      * configuration changes.
+     *
+     * @deprecated use {@link #retainNestedNonConfig()} to also track retained
+     *             nested child fragments
      */
     public List<Fragment> retainNonConfig() {
+        return mHost.mFragmentManager.retainNonConfig().getFragments();
+    }
+
+    /**
+     * Returns a nested tree of Fragments that have opted to retain their instance across
+     * configuration changes.
+     */
+    public FragmentManagerNonConfig retainNestedNonConfig() {
         return mHost.mFragmentManager.retainNonConfig();
     }
 
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 32fec84..4c8761c 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1503,6 +1503,26 @@
         }
     }
 
+    public void execSingleAction(Runnable action, boolean allowStateLoss) {
+        if (mExecutingActions) {
+            throw new IllegalStateException("FragmentManager is already executing transactions");
+        }
+
+        if (Looper.myLooper() != mHost.getHandler().getLooper()) {
+            throw new IllegalStateException("Must be called from main thread of fragment host");
+        }
+
+        if (allowStateLoss) {
+            checkStateLoss();
+        }
+
+        mExecutingActions = true;
+        action.run();
+        mExecutingActions = false;
+
+        doPendingDeferredStart();
+    }
+
     /**
      * Only call from main thread!
      */
@@ -1543,6 +1563,12 @@
             didSomething = true;
         }
 
+        doPendingDeferredStart();
+
+        return didSomething;
+    }
+
+    void doPendingDeferredStart() {
         if (mHavePendingDeferredStart) {
             boolean loadersRunning = false;
             for (int i=0; i<mActive.size(); i++) {
@@ -1556,7 +1582,6 @@
                 startPendingDeferredFragments();
             }
         }
-        return didSomething;
     }
 
     void reportBackStackChanged() {
@@ -1647,24 +1672,47 @@
         }
         return true;
     }
-    
-    ArrayList<Fragment> retainNonConfig() {
+
+    FragmentManagerNonConfig retainNonConfig() {
         ArrayList<Fragment> fragments = null;
+        ArrayList<FragmentManagerNonConfig> childFragments = null;
         if (mActive != null) {
             for (int i=0; i<mActive.size(); i++) {
                 Fragment f = mActive.get(i);
-                if (f != null && f.mRetainInstance) {
-                    if (fragments == null) {
-                        fragments = new ArrayList<Fragment>();
+                if (f != null) {
+                    if (f.mRetainInstance) {
+                        if (fragments == null) {
+                            fragments = new ArrayList<>();
+                        }
+                        fragments.add(f);
+                        f.mRetaining = true;
+                        f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
+                        if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                     }
-                    fragments.add(f);
-                    f.mRetaining = true;
-                    f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
-                    if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
+                    boolean addedChild = false;
+                    if (f.mChildFragmentManager != null) {
+                        FragmentManagerNonConfig child = f.mChildFragmentManager.retainNonConfig();
+                        if (child != null) {
+                            if (childFragments == null) {
+                                childFragments = new ArrayList<>();
+                                for (int j = 0; j < i; j++) {
+                                    childFragments.add(null);
+                                }
+                            }
+                            childFragments.add(child);
+                            addedChild = true;
+                        }
+                    }
+                    if (childFragments != null && !addedChild) {
+                        childFragments.add(null);
+                    }
                 }
             }
         }
-        return fragments;
+        if (fragments == null && childFragments == null) {
+            return null;
+        }
+        return new FragmentManagerNonConfig(fragments, childFragments);
     }
     
     void saveFragmentViewState(Fragment f) {
@@ -1821,18 +1869,23 @@
         return fms;
     }
     
-    void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
+    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
         // If there is no saved state at all, then there can not be
         // any nonConfig fragments either, so that is that.
         if (state == null) return;
         FragmentManagerState fms = (FragmentManagerState)state;
         if (fms.mActive == null) return;
-        
+
+        List<FragmentManagerNonConfig> childNonConfigs = null;
+
         // First re-attach any non-config instances we are retaining back
         // to their saved state, so we don't try to instantiate them again.
         if (nonConfig != null) {
-            for (int i=0; i<nonConfig.size(); i++) {
-                Fragment f = nonConfig.get(i);
+            List<Fragment> nonConfigFragments = nonConfig.getFragments();
+            childNonConfigs = nonConfig.getChildNonConfigs();
+            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
+            for (int i = 0; i < count; i++) {
+                Fragment f = nonConfigFragments.get(i);
                 if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
                 FragmentState fs = fms.mActive[f.mIndex];
                 fs.mInstance = f;
@@ -1852,14 +1905,18 @@
         
         // Build the full list of active fragments, instantiating them from
         // their saved state.
-        mActive = new ArrayList<Fragment>(fms.mActive.length);
+        mActive = new ArrayList<>(fms.mActive.length);
         if (mAvailIndices != null) {
             mAvailIndices.clear();
         }
         for (int i=0; i<fms.mActive.length; i++) {
             FragmentState fs = fms.mActive[i];
             if (fs != null) {
-                Fragment f = fs.instantiate(mHost, mParent);
+                FragmentManagerNonConfig childNonConfig = null;
+                if (childNonConfigs != null && i < childNonConfigs.size()) {
+                    childNonConfig = childNonConfigs.get(i);
+                }
+                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
                 if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                 mActive.add(f);
                 // Now that the fragment is instantiated (or came from being
@@ -1869,7 +1926,7 @@
             } else {
                 mActive.add(null);
                 if (mAvailIndices == null) {
-                    mAvailIndices = new ArrayList<Integer>();
+                    mAvailIndices = new ArrayList<>();
                 }
                 if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
                 mAvailIndices.add(i);
@@ -1878,8 +1935,10 @@
         
         // Update the target of all retained fragments.
         if (nonConfig != null) {
-            for (int i=0; i<nonConfig.size(); i++) {
-                Fragment f = nonConfig.get(i);
+            List<Fragment> nonConfigFragments = nonConfig.getFragments();
+            final int count = nonConfigFragments != null ? nonConfigFragments.size() : 0;
+            for (int i = 0; i < count; i++) {
+                Fragment f = nonConfigFragments.get(i);
                 if (f.mTargetIndex >= 0) {
                     if (f.mTargetIndex < mActive.size()) {
                         f.mTarget = mActive.get(f.mTargetIndex);
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
new file mode 100644
index 0000000..50d3797
--- /dev/null
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.app;
+
+import java.util.List;
+
+/**
+ * FragmentManagerNonConfig stores the retained instance fragments across
+ * activity recreation events.
+ *
+ * <p>Apps should treat objects of this type as opaque, returned by
+ * and passed to the state save and restore process for fragments in
+ * {@link FragmentController#retainNonConfig()} and
+ * {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ */
+public class FragmentManagerNonConfig {
+    private final List<Fragment> mFragments;
+    private final List<FragmentManagerNonConfig> mChildNonConfigs;
+
+    FragmentManagerNonConfig(List<Fragment> fragments,
+            List<FragmentManagerNonConfig> childNonConfigs) {
+        mFragments = fragments;
+        mChildNonConfigs = childNonConfigs;
+    }
+
+    /**
+     * @return the retained instance fragments returned by a FragmentManager
+     */
+    List<Fragment> getFragments() {
+        return mFragments;
+    }
+
+    /**
+     * @return the FragmentManagerNonConfigs from any applicable fragment's child FragmentManager
+     */
+    List<FragmentManagerNonConfig> getChildNonConfigs() {
+        return mChildNonConfigs;
+    }
+}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 0249cb2..e435580 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -284,4 +284,45 @@
      * to change unexpectedly on the user.
      */
     public abstract int commitAllowingStateLoss();
+
+    /**
+     * Commits this transaction synchronously. Any added fragments will be
+     * initialized and brought completely to the lifecycle state of their host
+     * and any removed fragments will be torn down accordingly before this
+     * call returns. Committing a transaction in this way allows fragments
+     * to be added as dedicated, encapsulated components that monitor the
+     * lifecycle state of their host while providing firmer ordering guarantees
+     * around when those fragments are fully initialized and ready. Fragments
+     * that manage views will have those views created and attached.
+     *
+     * <p>Calling <code>commitNow</code> is preferable to calling
+     * {@link #commit()} followed by {@link FragmentManager#executePendingTransactions()}
+     * as the latter will have the side effect of attempting to commit <em>all</em>
+     * currently pending transactions whether that is the desired behavior
+     * or not.</p>
+     *
+     * <p>Transactions committed in this way may not be added to the
+     * FragmentManager's back stack, as doing so would break other expected
+     * ordering guarantees for other asynchronously committed transactions.
+     * This method will throw {@link IllegalStateException} if the transaction
+     * previously requested to be added to the back stack with
+     * {@link #addToBackStack(String)}.</p>
+     *
+     * <p class="note">A transaction can only be committed with this method
+     * prior to its containing activity saving its state.  If the commit is
+     * attempted after that point, an exception will be thrown.  This is
+     * because the state after the commit can be lost if the activity needs to
+     * be restored from its state.  See {@link #commitAllowingStateLoss()} for
+     * situations where it may be okay to lose the commit.</p>
+     */
+    public abstract void commitNow();
+
+    /**
+     * Like {@link #commitNow} but allows the commit to be executed after an
+     * activity's state is saved.  This is dangerous because the commit can
+     * be lost if the activity needs to later be restored from its state, so
+     * this should only be used for cases where it is okay for the UI state
+     * to change unexpectedly on the user.
+     */
+    public abstract void commitNowAllowingStateLoss();
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f5e7d78..b5ca6ee 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -426,7 +426,7 @@
     // Multi-user APIs
     public boolean switchUser(int userid) throws RemoteException;
     public boolean startUserInBackground(int userid) throws RemoteException;
-    public boolean unlockUser(int userid, byte[] token) throws RemoteException;
+    public boolean unlockUser(int userid, byte[] token, byte[] secret) throws RemoteException;
     public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException;
     public UserInfo getCurrentUser() throws RemoteException;
     public boolean isUserRunning(int userid, int flags) throws RemoteException;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1fea665..c79dae5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -181,7 +181,7 @@
     public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
     
     /**
-     * Value for {@link #flags}: this is set of the application has specified
+     * Value for {@link #flags}: this is set if the application has specified
      * {@link android.R.styleable#AndroidManifestApplication_testOnly
      * android:testOnly} to be true.
      */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index aac0043..08deedb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4489,7 +4489,7 @@
 
             PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
-                parser.collectCertificates(pkg, 0);
+                PackageParser.collectCertificates(pkg, 0);
             }
             PackageUserState state = new PackageUserState();
             return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3802db8..72ace15 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -118,6 +118,8 @@
     private static final boolean DEBUG_PARSER = false;
     private static final boolean DEBUG_BACKUP = false;
 
+    private static final int MAX_PACKAGES_PER_APK = 5;
+
     // TODO: switch outError users to PackageParserException
     // TODO: refactor "codePath" to "apkPath"
 
@@ -133,6 +135,50 @@
     /** Path prefix for apps on expanded storage */
     private static final String MNT_EXPAND = "/mnt/expand/";
 
+    private static final String TAG_MANIFEST = "manifest";
+    private static final String TAG_APPLICATION = "application";
+    private static final String TAG_OVERLAY = "overlay";
+    private static final String TAG_KEY_SETS = "key-sets";
+    private static final String TAG_PERMISSION_GROUP = "permission-group";
+    private static final String TAG_PERMISSION = "permission";
+    private static final String TAG_PERMISSION_TREE = "permission-tree";
+    private static final String TAG_USES_PERMISSION = "uses-permission";
+    private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+    private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+    private static final String TAG_USES_CONFIGURATION = "uses-configuration";
+    private static final String TAG_USES_FEATURE = "uses-feature";
+    private static final String TAG_FEATURE_GROUP = "feature-group";
+    private static final String TAG_USES_SDK = "uses-sdk";
+    private static final String TAG_SUPPORT_SCREENS = "supports-screens";
+    private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+    private static final String TAG_INSTRUMENTATION = "instrumentation";
+    private static final String TAG_ORIGINAL_PACKAGE = "original-package";
+    private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+    private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+    private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+    private static final String TAG_SUPPORTS_INPUT = "supports-input";
+    private static final String TAG_EAT_COMMENT = "eat-comment";
+    private static final String TAG_PACKAGE = "package";
+
+    // These are the tags supported by child packages
+    private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
+    static {
+        CHILD_PACKAGE_TAGS.add(TAG_APPLICATION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE);
+        CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_SDK);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE);
+        CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
+        CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+    }
+
     /** @hide */
     public static class NewPermissionInfo {
         public final String name;
@@ -796,8 +842,9 @@
                 }
             }
 
-            pkg.codePath = packageDir.getAbsolutePath();
-            pkg.cpuAbiOverride = lite.abiOverride;
+            pkg.setCodePath(packageDir.getAbsolutePath());
+            pkg.setCpuAbiOverride(lite.abiOverride);
+
             return pkg;
         } finally {
             IoUtils.closeQuietly(assets);
@@ -827,8 +874,8 @@
         final AssetManager assets = new AssetManager();
         try {
             final Package pkg = parseBaseApk(apkFile, assets, flags);
-            pkg.codePath = apkFile.getAbsolutePath();
-            pkg.cpuAbiOverride = lite.abiOverride;
+            pkg.setCodePath(apkFile.getAbsolutePath());
+            pkg.setCpuAbiOverride(lite.abiOverride);
             return pkg;
         } finally {
             IoUtils.closeQuietly(assets);
@@ -885,10 +932,10 @@
                         apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
             }
 
-            pkg.volumeUuid = volumeUuid;
-            pkg.applicationInfo.volumeUuid = volumeUuid;
-            pkg.baseCodePath = apkPath;
-            pkg.mSignatures = null;
+            pkg.setVolumeUuid(volumeUuid);
+            pkg.setApplicationVolumeUuid(volumeUuid);
+            pkg.setBaseCodePath(apkPath);
+            pkg.setSignatures(null);
 
             return pkg;
 
@@ -951,7 +998,7 @@
         AttributeSet attrs = parser;
 
         // We parsed manifest tag earlier; just skip past it
-        parsePackageSplitNames(parser, attrs, flags);
+        parsePackageSplitNames(parser, attrs);
 
         mParseInstrumentationArgs = null;
         mParseActivityArgs = null;
@@ -984,7 +1031,7 @@
                 }
 
                 foundApp = true;
-                if (!parseSplitApplication(pkg, res, parser, attrs, flags, splitIndex, outError)) {
+                if (!parseSplitApplication(pkg, res, parser, flags, splitIndex, outError)) {
                     return null;
                 }
 
@@ -1016,7 +1063,18 @@
      * populating {@link Package#mSignatures}. Also asserts that all APK
      * contents are signed correctly and consistently.
      */
-    public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+    public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+        collectCertificatesInternal(pkg, parseFlags);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            Package childPkg = pkg.childPackages.get(i);
+            childPkg.mCertificates = pkg.mCertificates;
+            childPkg.mSignatures = pkg.mSignatures;
+            childPkg.mSigningKeys = pkg.mSigningKeys;
+        }
+    }
+
+    private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException {
         pkg.mCertificates = null;
         pkg.mSignatures = null;
         pkg.mSigningKeys = null;
@@ -1269,7 +1327,7 @@
     }
 
     private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
-            AttributeSet attrs, int flags) throws IOException, XmlPullParserException,
+            AttributeSet attrs) throws IOException, XmlPullParserException,
             PackageParserException {
 
         int type;
@@ -1281,7 +1339,7 @@
             throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No start tag found");
         }
-        if (!parser.getName().equals("manifest")) {
+        if (!parser.getName().equals(TAG_MANIFEST)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No <manifest> tag");
         }
@@ -1315,7 +1373,7 @@
     private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
             AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
             XmlPullParserException, PackageParserException {
-        final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
+        final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         int versionCode = 0;
@@ -1391,46 +1449,120 @@
     }
 
     /**
-     * Parse the manifest of a <em>base APK</em>.
-     * <p>
-     * When adding new features, carefully consider if they should also be
-     * supported by split APKs.
+     * Parses a child package and adds it to the parent if successful. If you add
+     * new tags that need to be supported by child packages make sure to add them
+     * to {@link #CHILD_PACKAGE_TAGS}.
+     *
+     * @param parentPkg The parent that contains the child
+     * @param res Resources against which to resolve values
+     * @param parser Parser of the manifest
+     * @param flags Flags about how to parse
+     * @param outError Human readable error if parsing fails
+     * @return True of parsing succeeded.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private boolean parseBaseApkChild(Package parentPkg, Resources res, XmlResourceParser parser,
+            int flags, String[] outError) throws XmlPullParserException, IOException {
+        // Let ppl not abuse this mechanism by limiting the packages per APK
+        if (parentPkg.childPackages != null && parentPkg.childPackages.size() + 2
+                > MAX_PACKAGES_PER_APK) {
+            outError[0] = "Maximum number of packages per APK is: " + MAX_PACKAGES_PER_APK;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Make sure we have a valid child package name
+        String childPackageName = parser.getAttributeValue(null, "package");
+        if (validateName(childPackageName, true, false) != null) {
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+            return false;
+        }
+
+        // Child packages must be unique
+        if (childPackageName.equals(parentPkg.packageName)) {
+            String message = "Child package name cannot be equal to parent package name: "
+                    + parentPkg.packageName;
+            Slog.w(TAG, message);
+            outError[0] = message;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Child packages must be unique
+        if (parentPkg.hasChildPackage(childPackageName)) {
+            String message = "Duplicate child package:" + childPackageName;
+            Slog.w(TAG, message);
+            outError[0] = message;
+            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+            return false;
+        }
+
+        // Go ahead and parse the child
+        Package childPkg = new Package(childPackageName);
+
+        // Child package inherits parent version code/name/target SDK
+        childPkg.mVersionCode = parentPkg.mVersionCode;
+        childPkg.baseRevisionCode = parentPkg.baseRevisionCode;
+        childPkg.mVersionName = parentPkg.mVersionName;
+        childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion;
+
+        childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError);
+        if (childPkg == null) {
+            // If we got null then error was set during child parsing
+            return false;
+        }
+
+        // Set the parent-child relation
+        if (parentPkg.childPackages == null) {
+            parentPkg.childPackages = new ArrayList<>();
+        }
+        parentPkg.childPackages.add(childPkg);
+        childPkg.parentPackage = parentPkg;
+
+        return true;
+    }
+
+    /**
+     * Parse the manifest of a <em>base APK</em>. When adding new features you
+     * need to consider whether they should be supported by split APKs and child
+     * packages.
+     *
+     * @param res The resources from which to resolve values
+     * @param parser The manifest parser
+     * @param flags Flags how to parse
+     * @param outError Human readable error message
+     * @return Parsed package or null on error.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
      */
     private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
             String[] outError) throws XmlPullParserException, IOException {
-        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
-
-        AttributeSet attrs = parser;
-
-        mParseInstrumentationArgs = null;
-        mParseActivityArgs = null;
-        mParseServiceArgs = null;
-        mParseProviderArgs = null;
-
-        final String pkgName;
         final String splitName;
+        final String pkgName;
+
         try {
-            Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
+            Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
             pkgName = packageSplit.first;
             splitName = packageSplit.second;
+
+            if (!TextUtils.isEmpty(splitName)) {
+                outError[0] = "Expected base APK, but found split " + splitName;
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+                return null;
+            }
         } catch (PackageParserException e) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
             return null;
         }
 
-        int type;
-
-        if (!TextUtils.isEmpty(splitName)) {
-            outError[0] = "Expected base APK, but found split " + splitName;
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
-            return null;
-        }
-
         final Package pkg = new Package(pkgName);
-        boolean foundApp = false;
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifest);
+
         pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
         pkg.baseRevisionCode = sa.getInteger(
@@ -1440,11 +1572,53 @@
         if (pkg.mVersionName != null) {
             pkg.mVersionName = pkg.mVersionName.intern();
         }
+
+        pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+
+        sa.recycle();
+
+        return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
+    }
+
+    /**
+     * This is the common parsing routing for handling parent and child
+     * packages in a base APK. The difference between parent and child
+     * parsing is that some targs are not supported by child packages as
+     * well as some manifest attributes are ignored. The implementation
+     * assumes the calling code already handled the manifest tag if needed
+     * (this applies to the parent only).
+     *
+     * @param pkg The package which to populate
+     * @param acceptedTags Which tags to handle, null to handle all
+     * @param res Resources against which to resolve values
+     * @param parser Parser of the manifest
+     * @param flags Flags about how to parse
+     * @param outError Human readable error if parsing fails
+     * @return The package if parsing succeeded or null.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
+            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
+            IOException {
+        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+        mParseInstrumentationArgs = null;
+        mParseActivityArgs = null;
+        mParseServiceArgs = null;
+        mParseProviderArgs = null;
+
+        int type;
+        boolean foundApp = false;
+
+        TypedArray sa = res.obtainAttributes(parser,
+                com.android.internal.R.styleable.AndroidManifest);
+
         String str = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
         if (str != null && str.length() > 0) {
             String nameError = validateName(str, true, false);
-            if (nameError != null && !"android".equals(pkgName)) {
+            if (nameError != null && !"android".equals(pkg.packageName)) {
                 outError[0] = "<manifest> specifies bad sharedUserId name \""
                     + str + "\": " + nameError;
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
@@ -1460,9 +1634,6 @@
                 PARSE_DEFAULT_INSTALL_LOCATION);
         pkg.applicationInfo.installLocation = pkg.installLocation;
 
-        pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false);
-
-        sa.recycle();
 
         /* Set the global "forward lock" flag */
         if ((flags & PARSE_FORWARD_LOCK) != 0) {
@@ -1494,7 +1665,16 @@
             }
 
             String tagName = parser.getName();
-            if (tagName.equals("application")) {
+
+            if (acceptedTags != null && !acceptedTags.contains(tagName)) {
+                Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+                        + tagName + " at " + mArchiveSourcePath + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+
+            if (tagName.equals(TAG_APPLICATION)) {
                 if (foundApp) {
                     if (RIGID_PARSER) {
                         outError[0] = "<manifest> has more than one <application>";
@@ -1508,13 +1688,13 @@
                 }
 
                 foundApp = true;
-                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
+                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                     return null;
                 }
-            } else if (tagName.equals("overlay")) {
+            } else if (tagName.equals(TAG_OVERLAY)) {
                 pkg.mTrustedOverlay = trustedOverlay;
 
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
@@ -1536,34 +1716,34 @@
                 }
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("key-sets")) {
-                if (!parseKeySets(pkg, res, parser, attrs, outError)) {
+            } else if (tagName.equals(TAG_KEY_SETS)) {
+                if (!parseKeySets(pkg, res, parser, outError)) {
                     return null;
                 }
-            } else if (tagName.equals("permission-group")) {
-                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
+            } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
+                if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
                     return null;
                 }
-            } else if (tagName.equals("permission")) {
-                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
+            } else if (tagName.equals(TAG_PERMISSION)) {
+                if (parsePermission(pkg, res, parser, outError) == null) {
                     return null;
                 }
-            } else if (tagName.equals("permission-tree")) {
-                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
+            } else if (tagName.equals(TAG_PERMISSION_TREE)) {
+                if (parsePermissionTree(pkg, res, parser, outError) == null) {
                     return null;
                 }
-            } else if (tagName.equals("uses-permission")) {
-                if (!parseUsesPermission(pkg, res, parser, attrs)) {
+            } else if (tagName.equals(TAG_USES_PERMISSION)) {
+                if (!parseUsesPermission(pkg, res, parser)) {
                     return null;
                 }
-            } else if (tagName.equals("uses-permission-sdk-m")
-                    || tagName.equals("uses-permission-sdk-23")) {
-                if (!parseUsesPermission(pkg, res, parser, attrs)) {
+            } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
+                    || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
+                if (!parseUsesPermission(pkg, res, parser)) {
                     return null;
                 }
-            } else if (tagName.equals("uses-configuration")) {
+            } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
                 ConfigurationInfo cPref = new ConfigurationInfo();
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
                 cPref.reqTouchScreen = sa.getInt(
                         com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
@@ -1589,8 +1769,8 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("uses-feature")) {
-                FeatureInfo fi = parseUsesFeature(res, attrs);
+            } else if (tagName.equals(TAG_USES_FEATURE)) {
+                FeatureInfo fi = parseUsesFeature(res, parser);
                 pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
 
                 if (fi.name == null) {
@@ -1601,7 +1781,7 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("feature-group")) {
+            } else if (tagName.equals(TAG_FEATURE_GROUP)) {
                 FeatureGroupInfo group = new FeatureGroupInfo();
                 ArrayList<FeatureInfo> features = null;
                 final int innerDepth = parser.getDepth();
@@ -1613,7 +1793,7 @@
 
                     final String innerTagName = parser.getName();
                     if (innerTagName.equals("uses-feature")) {
-                        FeatureInfo featureInfo = parseUsesFeature(res, attrs);
+                        FeatureInfo featureInfo = parseUsesFeature(res, parser);
                         // FeatureGroups are stricter and mandate that
                         // any <uses-feature> declared are mandatory.
                         featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
@@ -1632,9 +1812,9 @@
                 }
                 pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
 
-            } else if (tagName.equals("uses-sdk")) {
+            } else if (tagName.equals(TAG_USES_SDK)) {
                 if (SDK_VERSION > 0) {
-                    sa = res.obtainAttributes(attrs,
+                    sa = res.obtainAttributes(parser,
                             com.android.internal.R.styleable.AndroidManifestUsesSdk);
 
                     int minVers = 0;
@@ -1723,8 +1903,8 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("supports-screens")) {
-                sa = res.obtainAttributes(attrs,
+            } else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestSupportsScreens);
 
                 pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
@@ -1762,8 +1942,8 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("protected-broadcast")) {
-                sa = res.obtainAttributes(attrs,
+            } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
 
                 // Note: don't allow this value to be a reference to a resource
@@ -1784,13 +1964,12 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("instrumentation")) {
-                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
+            } else if (tagName.equals(TAG_INSTRUMENTATION)) {
+                if (parseInstrumentation(pkg, res, parser, outError) == null) {
                     return null;
                 }
-
-            } else if (tagName.equals("original-package")) {
-                sa = res.obtainAttributes(attrs,
+            } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestOriginalPackage);
 
                 String orig =sa.getNonConfigurationString(
@@ -1807,8 +1986,8 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("adopt-permissions")) {
-                sa = res.obtainAttributes(attrs,
+            } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestOriginalPackage);
 
                 String name = sa.getNonConfigurationString(
@@ -1825,24 +2004,29 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("uses-gl-texture")) {
+            } else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
                 // Just skip this tag
                 XmlUtils.skipCurrentTag(parser);
                 continue;
 
-            } else if (tagName.equals("compatible-screens")) {
+            } else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
                 // Just skip this tag
                 XmlUtils.skipCurrentTag(parser);
                 continue;
-            } else if (tagName.equals("supports-input")) {
+            } else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
                 XmlUtils.skipCurrentTag(parser);
                 continue;
 
-            } else if (tagName.equals("eat-comment")) {
+            } else if (tagName.equals(TAG_EAT_COMMENT)) {
                 // Just skip this tag
                 XmlUtils.skipCurrentTag(parser);
                 continue;
 
+            } else if (tagName.equals(TAG_PACKAGE)) {
+                if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
+                    // If parsing a child failed the error is already set
+                    return null;
+                }
             } else if (RIGID_PARSER) {
                 outError[0] = "Bad element under <manifest>: "
                     + parser.getName();
@@ -1956,9 +2140,9 @@
         return fi;
     }
 
-    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
-            AttributeSet attrs) throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestUsesPermission);
 
         // Note: don't allow this value to be a reference to a resource
@@ -2078,7 +2262,7 @@
     }
 
     private boolean parseKeySets(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, String[] outError)
+            XmlResourceParser parser, String[] outError)
             throws XmlPullParserException, IOException {
         // we've encountered the 'key-sets' tag
         // all the keys and keysets that we want must be defined here
@@ -2108,7 +2292,7 @@
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
                 }
-                final TypedArray sa = res.obtainAttributes(attrs,
+                final TypedArray sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestKeySet);
                 final String keysetName = sa.getNonResourceString(
                     com.android.internal.R.styleable.AndroidManifestKeySet_name);
@@ -2123,7 +2307,7 @@
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
                 }
-                final TypedArray sa = res.obtainAttributes(attrs,
+                final TypedArray sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestPublicKey);
                 final String publicKeyName = sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestPublicKey_name);
@@ -2164,7 +2348,7 @@
                 sa.recycle();
                 XmlUtils.skipCurrentTag(parser);
             } else if (tagName.equals("upgrade-key-set")) {
-                final TypedArray sa = res.obtainAttributes(attrs,
+                final TypedArray sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUpgradeKeySet);
                 String name = sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestUpgradeKeySet_name);
@@ -2223,11 +2407,11 @@
     }
 
     private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
-            XmlPullParser parser, AttributeSet attrs, String[] outError)
-        throws XmlPullParserException, IOException {
+            XmlResourceParser parser, String[] outError)
+            throws XmlPullParserException, IOException {
         PermissionGroup perm = new PermissionGroup(owner);
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup);
 
         if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2255,7 +2439,7 @@
 
         sa.recycle();
 
-        if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
+        if (!parseAllMetaData(res, parser, "<permission-group>", perm,
                 outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -2267,11 +2451,11 @@
     }
 
     private Permission parsePermission(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, String[] outError)
+            XmlResourceParser parser, String[] outError)
         throws XmlPullParserException, IOException {
         Permission perm = new Permission(owner);
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestPermission);
 
         if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2325,8 +2509,7 @@
             }
         }
 
-        if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
-                outError)) {
+        if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
         }
@@ -2337,11 +2520,11 @@
     }
 
     private Permission parsePermissionTree(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, String[] outError)
+            XmlResourceParser parser, String[] outError)
         throws XmlPullParserException, IOException {
         Permission perm = new Permission(owner);
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestPermissionTree);
 
         if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2373,7 +2556,7 @@
         perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
         perm.tree = true;
 
-        if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm,
+        if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
                 outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -2385,9 +2568,9 @@
     }
 
     private Instrumentation parseInstrumentation(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, String[] outError)
-        throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+            XmlResourceParser parser, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestInstrumentation);
 
         if (mParseInstrumentationArgs == null) {
@@ -2433,7 +2616,7 @@
             return null;
         }
 
-        if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a,
+        if (!parseAllMetaData(res, parser, "<instrumentation>", a,
                 outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -2452,12 +2635,12 @@
      * supported by split APKs.
      */
     private boolean parseBaseApplication(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+            XmlResourceParser parser, int flags, String[] outError)
         throws XmlPullParserException, IOException {
         final ApplicationInfo ai = owner.applicationInfo;
         final String pkgName = owner.applicationInfo.packageName;
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestApplication);
 
         String name = sa.getNonConfigurationString(
@@ -2608,10 +2791,13 @@
             ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
-                false)) {
-            ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+        // The parent package controls installation, hence specify test only installs.
+        if (owner.parentPackage == null) {
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
+                    false)) {
+                ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+            }
         }
 
         if (sa.getBoolean(
@@ -2736,7 +2922,7 @@
 
             String tagName = parser.getName();
             if (tagName.equals("activity")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+                Activity a = parseActivity(owner, res, parser, flags, outError, false,
                         owner.baseHardwareAccelerated);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2746,7 +2932,7 @@
                 owner.activities.add(a);
 
             } else if (tagName.equals("receiver")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
+                Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2755,7 +2941,7 @@
                 owner.receivers.add(a);
 
             } else if (tagName.equals("service")) {
-                Service s = parseService(owner, res, parser, attrs, flags, outError);
+                Service s = parseService(owner, res, parser, flags, outError);
                 if (s == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2764,7 +2950,7 @@
                 owner.services.add(s);
 
             } else if (tagName.equals("provider")) {
-                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
+                Provider p = parseProvider(owner, res, parser, flags, outError);
                 if (p == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2773,7 +2959,7 @@
                 owner.providers.add(p);
 
             } else if (tagName.equals("activity-alias")) {
-                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
+                Activity a = parseActivityAlias(owner, res, parser, flags, outError);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2785,14 +2971,14 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
+                if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
                         outError)) == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
                 }
 
             } else if (tagName.equals("library")) {
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestLibrary);
 
                 // Note: don't allow this value to be a reference to a resource
@@ -2812,7 +2998,7 @@
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals("uses-library")) {
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUsesLibrary);
 
                 // Note: don't allow this value to be a reference to a resource
@@ -2913,10 +3099,10 @@
      * of doing, so many valid features of a base APK have been carefully
      * omitted here.
      */
-    private boolean parseSplitApplication(Package owner, Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, int splitIndex, String[] outError)
+    private boolean parseSplitApplication(Package owner, Resources res, XmlResourceParser parser,
+            int flags, int splitIndex, String[] outError)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestApplication);
 
         if (sa.getBoolean(
@@ -2934,7 +3120,7 @@
 
             String tagName = parser.getName();
             if (tagName.equals("activity")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+                Activity a = parseActivity(owner, res, parser, flags, outError, false,
                         owner.baseHardwareAccelerated);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2944,7 +3130,7 @@
                 owner.activities.add(a);
 
             } else if (tagName.equals("receiver")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
+                Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2953,7 +3139,7 @@
                 owner.receivers.add(a);
 
             } else if (tagName.equals("service")) {
-                Service s = parseService(owner, res, parser, attrs, flags, outError);
+                Service s = parseService(owner, res, parser, flags, outError);
                 if (s == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2962,7 +3148,7 @@
                 owner.services.add(s);
 
             } else if (tagName.equals("provider")) {
-                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
+                Provider p = parseProvider(owner, res, parser, flags, outError);
                 if (p == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2971,7 +3157,7 @@
                 owner.providers.add(p);
 
             } else if (tagName.equals("activity-alias")) {
-                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
+                Activity a = parseActivityAlias(owner, res, parser, flags, outError);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -2983,14 +3169,14 @@
                 // note: application meta-data is stored off to the side, so it can
                 // remain null in the primary copy (we like to avoid extra copies because
                 // it can be large)
-                if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
+                if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
                         outError)) == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
                 }
 
             } else if (tagName.equals("uses-library")) {
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestUsesLibrary);
 
                 // Note: don't allow this value to be a reference to a resource
@@ -3086,10 +3272,10 @@
     }
 
     private Activity parseActivity(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
+            XmlResourceParser parser, int flags, String[] outError,
             boolean receiver, boolean hardwareAccelerated)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
 
         if (mParseActivityArgs == null) {
             mParseActivityArgs = new ParseComponentArgs(owner, outError,
@@ -3327,7 +3513,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
+                if (!parseIntent(res, parser, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3339,7 +3525,7 @@
                 }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
+                if (!parseIntent(res, parser, false, false, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3353,12 +3539,12 @@
                     owner.preferredActivityFilters.add(intent);
                 }
             } else if (parser.getName().equals("meta-data")) {
-                if ((a.metaData = parseMetaData(res, parser, attrs, a.metaData,
+                if ((a.metaData = parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
                     return null;
                 }
             } else if (!receiver && parser.getName().equals("layout")) {
-                parseLayout(res, attrs, a);
+                parseLayout(res, parser, a);
             } else {
                 if (!RIGID_PARSER) {
                     Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
@@ -3432,9 +3618,9 @@
     }
 
     private Activity parseActivityAlias(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+            XmlResourceParser parser, int flags, String[] outError)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestActivityAlias);
 
         String targetActivity = sa.getNonConfigurationString(
@@ -3565,7 +3751,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
+                if (!parseIntent(res, parser, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3576,7 +3762,7 @@
                     a.intents.add(intent);
                 }
             } else if (parser.getName().equals("meta-data")) {
-                if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
+                if ((a.metaData=parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
                     return null;
                 }
@@ -3602,9 +3788,9 @@
     }
 
     private Provider parseProvider(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+            XmlResourceParser parser, int flags, String[] outError)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestProvider);
 
         if (mParseProviderArgs == null) {
@@ -3731,7 +3917,7 @@
         }
         p.info.authority = cpname.intern();
 
-        if (!parseProviderTags(res, parser, attrs, p, outError)) {
+        if (!parseProviderTags(res, parser, p, outError)) {
             return null;
         }
 
@@ -3739,8 +3925,7 @@
     }
 
     private boolean parseProviderTags(Resources res,
-            XmlPullParser parser, AttributeSet attrs,
-            Provider outInfo, String[] outError)
+            XmlResourceParser parser, Provider outInfo, String[] outError)
             throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
@@ -3753,19 +3938,19 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
-                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
+                if (!parseIntent(res, parser, true, false, intent, outError)) {
                     return false;
                 }
                 outInfo.intents.add(intent);
 
             } else if (parser.getName().equals("meta-data")) {
-                if ((outInfo.metaData=parseMetaData(res, parser, attrs,
+                if ((outInfo.metaData=parseMetaData(res, parser,
                         outInfo.metaData, outError)) == null) {
                     return false;
                 }
 
             } else if (parser.getName().equals("grant-uri-permission")) {
-                TypedArray sa = res.obtainAttributes(attrs,
+                TypedArray sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
 
                 PatternMatcher pa = null;
@@ -3817,7 +4002,7 @@
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (parser.getName().equals("path-permission")) {
-                TypedArray sa = res.obtainAttributes(attrs,
+                TypedArray sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestPathPermission);
 
                 PathPermission pa = null;
@@ -3922,9 +4107,9 @@
     }
 
     private Service parseService(Package owner, Resources res,
-            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+            XmlResourceParser parser, int flags, String[] outError)
             throws XmlPullParserException, IOException {
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestService);
 
         if (mParseServiceArgs == null) {
@@ -4025,13 +4210,13 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ServiceIntentInfo intent = new ServiceIntentInfo(s);
-                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
+                if (!parseIntent(res, parser, true, false, intent, outError)) {
                     return null;
                 }
 
                 s.intents.add(intent);
             } else if (parser.getName().equals("meta-data")) {
-                if ((s.metaData=parseMetaData(res, parser, attrs, s.metaData,
+                if ((s.metaData=parseMetaData(res, parser, s.metaData,
                         outError)) == null) {
                     return null;
                 }
@@ -4056,10 +4241,8 @@
         return s;
     }
 
-    private boolean parseAllMetaData(Resources res,
-            XmlPullParser parser, AttributeSet attrs, String tag,
-            Component<?> outInfo, String[] outError)
-            throws XmlPullParserException, IOException {
+    private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
+            Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4070,7 +4253,7 @@
             }
 
             if (parser.getName().equals("meta-data")) {
-                if ((outInfo.metaData=parseMetaData(res, parser, attrs,
+                if ((outInfo.metaData=parseMetaData(res, parser,
                         outInfo.metaData, outError)) == null) {
                     return false;
                 }
@@ -4091,11 +4274,10 @@
     }
 
     private Bundle parseMetaData(Resources res,
-            XmlPullParser parser, AttributeSet attrs,
-            Bundle data, String[] outError)
+            XmlResourceParser parser, Bundle data, String[] outError)
             throws XmlPullParserException, IOException {
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestMetaData);
 
         if (data == null) {
@@ -4234,11 +4416,11 @@
     private static final String ANDROID_RESOURCES
             = "http://schemas.android.com/apk/res/android";
 
-    private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
+    private boolean parseIntent(Resources res, XmlResourceParser parser,
             boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
             throws XmlPullParserException, IOException {
 
-        TypedArray sa = res.obtainAttributes(attrs,
+        TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestIntentFilter);
 
         int priority = sa.getInt(
@@ -4278,7 +4460,7 @@
 
             String nodeName = parser.getName();
             if (nodeName.equals("action")) {
-                String value = attrs.getAttributeValue(
+                String value = parser.getAttributeValue(
                         ANDROID_RESOURCES, "name");
                 if (value == null || value == "") {
                     outError[0] = "No value supplied for <android:name>";
@@ -4288,7 +4470,7 @@
 
                 outInfo.addAction(value);
             } else if (nodeName.equals("category")) {
-                String value = attrs.getAttributeValue(
+                String value = parser.getAttributeValue(
                         ANDROID_RESOURCES, "name");
                 if (value == null || value == "") {
                     outError[0] = "No value supplied for <android:name>";
@@ -4299,7 +4481,7 @@
                 outInfo.addCategory(value);
 
             } else if (nodeName.equals("data")) {
-                sa = res.obtainAttributes(attrs,
+                sa = res.obtainAttributes(parser,
                         com.android.internal.R.styleable.AndroidManifestData);
 
                 String str = sa.getNonConfigurationString(
@@ -4464,6 +4646,9 @@
 
         public ArrayList<String> protectedBroadcasts;
 
+        public Package parentPackage;
+        public ArrayList<Package> childPackages;
+
         public ArrayList<String> libraryNames = null;
         public ArrayList<String> usesLibraries = null;
         public ArrayList<String> usesOptionalLibraries = null;
@@ -4562,6 +4747,141 @@
             applicationInfo.uid = -1;
         }
 
+        public void setApplicationVolumeUuid(String volumeUuid) {
+            this.applicationInfo.volumeUuid = volumeUuid;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.volumeUuid = volumeUuid;
+                }
+            }
+        }
+
+        public void setApplicationInfoCodePath(String codePath) {
+            this.applicationInfo.setCodePath(codePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setCodePath(codePath);
+                }
+            }
+        }
+
+        public void setApplicationInfoResourcePath(String resourcePath) {
+            this.applicationInfo.setResourcePath(resourcePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setResourcePath(resourcePath);
+                }
+            }
+        }
+
+        public void setApplicationInfoBaseResourcePath(String resourcePath) {
+            this.applicationInfo.setBaseResourcePath(resourcePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setBaseResourcePath(resourcePath);
+                }
+            }
+        }
+
+        public void setApplicationInfoBaseCodePath(String baseCodePath) {
+            this.applicationInfo.setBaseCodePath(baseCodePath);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.setBaseCodePath(baseCodePath);
+                }
+            }
+        }
+
+        public boolean hasChildPackage(String packageName) {
+            final int childCount = (childPackages != null) ? childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                if (childPackages.get(i).packageName.equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public void setApplicationInfoSplitCodePaths(String[] splitCodePaths) {
+            this.applicationInfo.setSplitCodePaths(splitCodePaths);
+            // Children have no splits
+        }
+
+        public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) {
+            this.applicationInfo.setSplitResourcePaths(resroucePaths);
+            // Children have no splits
+        }
+
+        public void setSplitCodePaths(String[] codePaths) {
+            this.splitCodePaths = codePaths;
+        }
+
+        public void setCodePath(String codePath) {
+            this.codePath = codePath;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).codePath = codePath;
+                }
+            }
+        }
+
+        public void setBaseCodePath(String baseCodePath) {
+            this.baseCodePath = baseCodePath;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).baseCodePath = baseCodePath;
+                }
+            }
+        }
+
+        public void setSignatures(Signature[] signatures) {
+            this.mSignatures = signatures;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).mSignatures = signatures;
+                }
+            }
+        }
+
+        public void setVolumeUuid(String volumeUuid) {
+            this.volumeUuid = volumeUuid;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).volumeUuid = volumeUuid;
+                }
+            }
+        }
+
+        public void setApplicationInfoFlags(int mask, int flags) {
+            applicationInfo.flags = (applicationInfo.flags & ~mask) | (mask & flags);
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).applicationInfo.flags =
+                            (applicationInfo.flags & ~mask) | (mask & flags);
+                }
+            }
+        }
+
+        public void setCpuAbiOverride(String cpuAbiOverride) {
+            this.cpuAbiOverride = cpuAbiOverride;
+            if (childPackages != null) {
+                final int packageCount = childPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    childPackages.get(i).cpuAbiOverride = cpuAbiOverride;
+                }
+            }
+        }
+
         public List<String> getAllCodePaths() {
             ArrayList<String> paths = new ArrayList<>();
             paths.add(baseCodePath);
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
new file mode 100644
index 0000000..80e6146
--- /dev/null
+++ b/core/java/android/os/UpdateEngine.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+import android.os.IUpdateEngine;
+import android.os.IUpdateEngineCallback;
+import android.os.RemoteException;
+
+import android.util.Log;
+
+/**
+ * UpdateEngine handles calls to the update engine which takes care of A/B OTA
+ * updates. It wraps up the update engine Binder APIs and exposes them as
+ * SystemApis, which will be called by system apps like GmsCore.
+ *
+ * The APIs defined in this class and UpdateEngineCallback class must be in
+ * sync with the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public class UpdateEngine {
+    private static final String TAG = "UpdateEngine";
+
+    private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService";
+
+    /**
+     * Error code from the update engine. Values must agree with the ones in
+     * system/update_engine/common/error_code.h.
+     */
+    @SystemApi
+    public static final class ErrorCodeConstants {
+        public static final int SUCCESS = 0;
+        public static final int ERROR = 1;
+        public static final int FILESYSTEM_COPIER_ERROR = 4;
+        public static final int POST_INSTALL_RUNNER_ERROR = 5;
+        public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6;
+        public static final int INSTALL_DEVICE_OPEN_ERROR = 7;
+        public static final int KERNEL_DEVICE_OPEN_ERROR = 8;
+        public static final int DOWNLOAD_TRANSFER_ERROR = 9;
+        public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
+        public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
+        public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+    }
+
+    /**
+     * Update status code from the update engine. Values must agree with the
+     * ones in system/update_engine/client_library/include/update_engine/update_status.h.
+     */
+    @SystemApi
+    public static final class UpdateStatusConstants {
+        public static final int IDLE = 0;
+        public static final int CHECKING_FOR_UPDATE = 1;
+        public static final int UPDATE_AVAILABLE = 2;
+        public static final int DOWNLOADING = 3;
+        public static final int VERIFYING = 4;
+        public static final int FINALIZING = 5;
+        public static final int UPDATED_NEED_REBOOT = 6;
+        public static final int REPORTING_ERROR_EVENT = 7;
+        public static final int ATTEMPTING_ROLLBACK = 8;
+        public static final int DISABLED = 9;
+    }
+
+    private IUpdateEngine mUpdateEngine;
+
+    @SystemApi
+    public UpdateEngine() {
+        mUpdateEngine = IUpdateEngine.Stub.asInterface(
+                ServiceManager.getService(UPDATE_ENGINE_SERVICE));
+    }
+
+    @SystemApi
+    public boolean bind(final UpdateEngineCallback callback, final Handler handler) throws RemoteException {
+        IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
+            @Override
+            public void onStatusUpdate(final int status, final float percent) {
+                if (handler != null) {
+                    handler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onStatusUpdate(status, percent);
+                        }
+                    });
+                } else {
+                    callback.onStatusUpdate(status, percent);
+                }
+            }
+
+            @Override
+            public void onPayloadApplicationComplete(final int errorCode) {
+                if (handler != null) {
+                    handler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            callback.onPayloadApplicationComplete(errorCode);
+                        }
+                    });
+                } else {
+                    callback.onPayloadApplicationComplete(errorCode);
+                }
+            }
+        };
+
+        return mUpdateEngine.bind(updateEngineCallback);
+    }
+
+    @SystemApi
+    public boolean bind(final UpdateEngineCallback callback) throws RemoteException {
+        return bind(callback, null);
+    }
+
+    @SystemApi
+    public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) throws RemoteException {
+        mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+    }
+
+    @SystemApi
+    public void cancel() throws RemoteException {
+        mUpdateEngine.cancel();
+    }
+
+    @SystemApi
+    public void suspend() throws RemoteException {
+        mUpdateEngine.suspend();
+    }
+
+    @SystemApi
+    public void resume() throws RemoteException {
+        mUpdateEngine.resume();
+    }
+}
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
new file mode 100644
index 0000000..b3b856f
--- /dev/null
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback function for UpdateEngine.
+ *
+ * The APIs defined in this class and UpdateEngine class must be in sync with
+ * the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class UpdateEngineCallback {
+
+    @SystemApi
+    public abstract void onStatusUpdate(int status, float percent);
+
+    @SystemApi
+    public abstract void onPayloadApplicationComplete(int errorCode);
+}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index dd8eb5f..fc440d2 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1233,7 +1233,8 @@
             }
 
             @Override
-            public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+            public void changeUserKey(int userId, int serialNumber,
+                    byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
@@ -1241,6 +1242,27 @@
                     _data.writeInt(userId);
                     _data.writeInt(serialNumber);
                     _data.writeByteArray(token);
+                    _data.writeByteArray(oldSecret);
+                    _data.writeByteArray(newSecret);
+                    mRemote.transact(Stub.TRANSACTION_changeUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public void unlockUserKey(int userId, int serialNumber,
+                    byte[] token, byte[] secret) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
+                    _data.writeInt(serialNumber);
+                    _data.writeByteArray(token);
+                    _data.writeByteArray(secret);
                     mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
                     _reply.readException();
                 } finally {
@@ -1448,6 +1470,8 @@
 
         static final int TRANSACTION_mountAppFuse = IBinder.FIRST_CALL_TRANSACTION + 69;
 
+        static final int TRANSACTION_changeUserKey = IBinder.FIRST_CALL_TRANSACTION + 70;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -2026,12 +2050,24 @@
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_changeUserKey: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userId = data.readInt();
+                    int serialNumber = data.readInt();
+                    byte[] token = data.createByteArray();
+                    byte[] oldSecret = data.createByteArray();
+                    byte[] newSecret = data.createByteArray();
+                    changeUserKey(userId, serialNumber, token, oldSecret, newSecret);
+                    reply.writeNoException();
+                    return true;
+                }
                 case TRANSACTION_unlockUserKey: {
                     data.enforceInterface(DESCRIPTOR);
                     int userId = data.readInt();
                     int serialNumber = data.readInt();
                     byte[] token = data.createByteArray();
-                    unlockUserKey(userId, serialNumber, token);
+                    byte[] secret = data.createByteArray();
+                    unlockUserKey(userId, serialNumber, token, secret);
                     reply.writeNoException();
                     return true;
                 }
@@ -2383,8 +2419,11 @@
     public void createUserKey(int userId, int serialNumber, boolean ephemeral)
             throws RemoteException;
     public void destroyUserKey(int userId) throws RemoteException;
+    public void changeUserKey(int userId, int serialNumber,
+            byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException;
 
-    public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+    public void unlockUserKey(int userId, int serialNumber,
+            byte[] token, byte[] secret) throws RemoteException;
     public void lockUserKey(int userId) throws RemoteException;
     public boolean isUserKeyUnlocked(int userId) throws RemoteException;
 
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b82638a..e7dfbd7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -991,9 +991,9 @@
     }
 
     /** {@hide} */
-    public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
         try {
-            mMountService.unlockUserKey(userId, serialNumber, token);
+            mMountService.unlockUserKey(userId, serialNumber, token, secret);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 287c696..b2e8d33 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -57,9 +57,9 @@
  * tag <code>fade</code>, along with the standard
  * attributes of {@link android.R.styleable#Fade} and
  * {@link android.R.styleable#Transition}.</p>
-
  */
 public class Fade extends Visibility {
+    static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha";
 
     private static boolean DBG = Transition.DBG && false;
 
@@ -105,6 +105,13 @@
         setMode(fadingMode);
     }
 
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        transitionValues.values.put(PROPNAME_TRANSITION_ALPHA,
+                transitionValues.view.getTransitionAlpha());
+    }
+
     /**
      * Utility method to handle creating and running the Animator.
      */
@@ -119,7 +126,6 @@
         }
         final FadeAnimatorListener listener = new FadeAnimatorListener(view);
         anim.addListener(listener);
-        anim.addPauseListener(listener);
         addListener(new TransitionListenerAdapter() {
             @Override
             public void onTransitionEnd(Transition transition) {
@@ -138,18 +144,28 @@
             Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
                     startView + ", " + view);
         }
-        return createAnimation(view, 0, 1);
+        float startAlpha = 0;
+        if (startValues != null) {
+            startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+            if (startAlpha == 1) {
+                startAlpha = 0;
+            }
+        }
+        return createAnimation(view, startAlpha, 1);
     }
 
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
             TransitionValues endValues) {
-        return createAnimation(view, 1, 0);
+        float startAlpha = 1;
+        if (startValues != null) {
+            startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+        }
+        return createAnimation(view, startAlpha, 0);
     }
 
     private static class FadeAnimatorListener extends AnimatorListenerAdapter {
         private final View mView;
-        private float mPausedAlpha = -1;
         private boolean mLayerTypeChanged = false;
 
         public FadeAnimatorListener(View view) {
@@ -171,16 +187,5 @@
                 mView.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedAlpha = mView.getTransitionAlpha();
-            mView.setTransitionAlpha(1);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mView.setTransitionAlpha(mPausedAlpha);
-        }
     }
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1740f07..5b9930b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -166,6 +166,7 @@
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
             int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
     void setAppVisibility(IBinder token, boolean visible);
+    void notifyAppStopped(IBinder token);
     void startAppFreezingScreen(IBinder token, int configChanges);
     void stopAppFreezingScreen(IBinder token, boolean force);
     void removeAppToken(IBinder token);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 127157b..f52b2907 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16582,11 +16582,12 @@
         RenderNode renderNode = null;
         Bitmap cache = null;
         int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
-        if (layerType == LAYER_TYPE_SOFTWARE
-                || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
-            // If not drawing with RenderNode, treat HW layers as SW
-            layerType = LAYER_TYPE_SOFTWARE;
-            buildDrawingCache(true);
+        if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
+             if (layerType != LAYER_TYPE_NONE) {
+                 // If not drawing with RenderNode, treat HW layers as SW
+                 layerType = LAYER_TYPE_SOFTWARE;
+                 buildDrawingCache(true);
+            }
             cache = getDrawingCache(true);
         }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3fe6b8e..c54ce80 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -387,6 +387,12 @@
     private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED = 0x10000000;
 
     /**
+     * When set, indicates that a call to showContextMenuForChild was made with explicit
+     * coordinates within the initiating child view.
+     */
+    private static final int FLAG_SHOW_CONTEXT_MENU_WITH_COORDS = 0x20000000;
+
+    /**
      * Indicates which types of drawing caches are to be kept in memory.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -756,11 +762,31 @@
 
     @Override
     public boolean showContextMenuForChild(View originalView) {
+        if (isShowingContextMenuWithCoords()) {
+            // We're being called for compatibility. Return false and let the version
+            // with coordinates recurse up.
+            return false;
+        }
         return mParent != null && mParent.showContextMenuForChild(originalView);
     }
 
+    /**
+     * @hide used internally for compatibility with existing app code only
+     */
+    public final boolean isShowingContextMenuWithCoords() {
+        return (mGroupFlags & FLAG_SHOW_CONTEXT_MENU_WITH_COORDS) != 0;
+    }
+
     @Override
     public boolean showContextMenuForChild(View originalView, float x, float y) {
+        try {
+            mGroupFlags |= FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
+            if (showContextMenuForChild(originalView)) {
+                return true;
+            }
+        } finally {
+            mGroupFlags &= ~FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
+        }
         return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
     }
 
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 1962be8..06afef2 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -192,6 +192,9 @@
      * the subclass is added directly to the window manager (for example,
      * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
      * then it should override this and show the context menu.
+     * <p>
+     * If a subclass overrides this method it should also override
+     * {@link #showContextMenuForChild(View)}.
      *
      * @param originalView the source view where the context menu was first
      *                     invoked
@@ -202,7 +205,7 @@
      * @return {@code true} if the context menu was shown, {@code false}
      *         otherwise
      */
-    public boolean showContextMenuForChild(View originalView, float x, float y);
+    boolean showContextMenuForChild(View originalView, float x, float y);
 
     /**
      * Have the parent populate the specified context menu if it has anything to
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7b6fa6d..b689564 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3247,6 +3247,9 @@
 
     @Override
     public boolean showContextMenuForChild(View originalView) {
+        if (isShowingContextMenuWithCoords()) {
+            return false;
+        }
         return showContextMenuForChildInternal(originalView, 0, 0, false);
     }
 
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index e31bbe9..1d242d3 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -65,6 +65,11 @@
     }
 
     @Override
+    public boolean getFreezesText() {
+        return true;
+    }
+
+    @Override
     protected boolean getDefaultEditable() {
         return true;
     }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3a61fcd..1826dd8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1851,8 +1851,7 @@
         updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped));
 
         if (mCursorCount == 2) {
-            updateCursorPosition(1, middle, bottom,
-                    layout.getSecondaryHorizontal(offset, clamped));
+            updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped));
         }
     }
 
@@ -2151,21 +2150,60 @@
         return mSelectionModifierCursorController;
     }
 
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public Drawable[] getCursorDrawable() {
+        return mCursorDrawable;
+    }
+
     private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
         if (mCursorDrawable[cursorIndex] == null)
             mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable(
                     mTextView.mCursorDrawableRes);
-
-        if (mTempRect == null) mTempRect = new Rect();
-        mCursorDrawable[cursorIndex].getPadding(mTempRect);
-        final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
-        horizontal = Math.max(0.5f, horizontal - 0.5f);
-        final int left = (int) (horizontal) - mTempRect.left;
-        mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+        final Drawable drawable = mCursorDrawable[cursorIndex];
+        final int left = clampCursorHorizontalPosition(drawable, horizontal);
+        final int width = drawable.getIntrinsicWidth();
+        drawable.setBounds(left, top - mTempRect.top, left + width,
                 bottom + mTempRect.bottom);
     }
 
     /**
+     * Return clamped position for the cursor. If the cursor is within the boundaries of the view,
+     * then it is offset with the left padding of the cursor drawable. If the cursor is at
+     * the beginning or the end of the text then its drawable edge is aligned with left or right of
+     * the view boundary.
+     *
+     * @param drawable   Cursor drawable.
+     * @param horizontal Horizontal position for the cursor.
+     * @return The clamped horizontal position for the cursor.
+     */
+    private final int clampCursorHorizontalPosition(final Drawable drawable, float
+            horizontal) {
+        horizontal = Math.max(0.5f, horizontal - 0.5f);
+        if (mTempRect == null) mTempRect = new Rect();
+        drawable.getPadding(mTempRect);
+        int scrollX = mTextView.getScrollX();
+        float horizontalDiff = horizontal - scrollX;
+        int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft()
+                - mTextView.getCompoundPaddingRight();
+
+        final int left;
+        if (horizontalDiff >= (viewClippedWidth - 1f)) {
+            // at the rightmost position
+            final int cursorWidth = drawable.getIntrinsicWidth();
+            left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right);
+        } else if (Math.abs(horizontalDiff) <= 1f) {
+            // at the leftmost position
+            left = scrollX - mTempRect.left;
+        } else {
+            left = (int) horizontal - mTempRect.left;
+        }
+        return left;
+    }
+
+    /**
      * Called by the framework in response to a text auto-correction (such as fixing a typo using a
      * a dictionary) from the current input method, provided by it calling
      * {@link InputConnection#commitCorrection} InputConnection.commitCorrection()}. The default
@@ -3919,8 +3957,8 @@
             final Layout layout = mTextView.getLayout();
             if (layout != null && oldDrawable != mDrawable && isShowing()) {
                 // Update popup window position.
-                mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
-                        getHorizontalOffset() + getCursorOffset());
+                mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
+                        getHorizontalOffset() + getCursorOffset();
                 mPositionX += mTextView.viewportToContentHorizontalOffset();
                 mPositionHasChanged = true;
                 updatePosition(mLastParentX, mLastParentY, false, false);
@@ -4049,8 +4087,8 @@
                 final int line = layout.getLineForOffset(offset);
                 mPrevLine = line;
 
-                mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
-                        getHorizontalOffset() + getCursorOffset());
+                mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
+                        getHorizontalOffset() + getCursorOffset();
                 mPositionY = layout.getLineBottom(line);
 
                 // Take TextView's padding and scroll into account.
@@ -4062,6 +4100,17 @@
             }
         }
 
+        /**
+         * Return the clamped horizontal position for the first cursor.
+         *
+         * @param layout Text layout.
+         * @param offset Character offset for the cursor.
+         * @return The clamped horizontal position for the cursor.
+         */
+        int getCursorHorizontalPosition(Layout layout, int offset) {
+            return (int) (layout.getPrimaryHorizontal(offset) - 0.5f);
+        }
+
         public void updatePosition(int parentPositionX, int parentPositionY,
                 boolean parentPositionChanged, boolean parentScrolled) {
             positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled);
@@ -4300,6 +4349,16 @@
         }
 
         @Override
+        int getCursorHorizontalPosition(Layout layout, int offset) {
+            final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null;
+            if (drawable != null) {
+                final float horizontal = layout.getPrimaryHorizontal(offset);
+                return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left;
+            }
+            return super.getCursorHorizontalPosition(layout, offset);
+        }
+
+        @Override
         public boolean onTouchEvent(MotionEvent ev) {
             final boolean result = super.onTouchEvent(ev);
 
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index a6ef572..7655f3d 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1160,6 +1160,9 @@
 
     @Override
     public boolean showContextMenuForChild(View originalView) {
+        if (isShowingContextMenuWithCoords()) {
+            return false;
+        }
         return showContextMenuForChildInternal(originalView, 0, 0, false);
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 712a04b..a5c1da9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4101,36 +4101,42 @@
         Parcelable superState = super.onSaveInstanceState();
 
         // Save state if we are forced to
-        boolean save = mFreezesText;
-        int start = 0;
-        int end = 0;
+        final boolean freezesText = getFreezesText();
+        boolean hasSelection = false;
+        int start = -1;
+        int end = -1;
 
         if (mText != null) {
             start = getSelectionStart();
             end = getSelectionEnd();
             if (start >= 0 || end >= 0) {
                 // Or save state if there is a selection
-                save = true;
+                hasSelection = true;
             }
         }
 
-        if (save) {
+        if (freezesText || hasSelection) {
             SavedState ss = new SavedState(superState);
-            // XXX Should also save the current scroll position!
-            ss.selStart = start;
-            ss.selEnd = end;
 
-            if (mText instanceof Spanned) {
-                Spannable sp = new SpannableStringBuilder(mText);
+            if (freezesText) {
+                if (mText instanceof Spanned) {
+                    final Spannable sp = new SpannableStringBuilder(mText);
 
-                if (mEditor != null) {
-                    removeMisspelledSpans(sp);
-                    sp.removeSpan(mEditor.mSuggestionRangeSpan);
+                    if (mEditor != null) {
+                        removeMisspelledSpans(sp);
+                        sp.removeSpan(mEditor.mSuggestionRangeSpan);
+                    }
+
+                    ss.text = sp;
+                } else {
+                    ss.text = mText.toString();
                 }
+            }
 
-                ss.text = sp;
-            } else {
-                ss.text = mText.toString();
+            if (hasSelection) {
+                // XXX Should also save the current scroll position!
+                ss.selStart = start;
+                ss.selEnd = end;
             }
 
             if (isFocused() && start >= 0 && end >= 0) {
@@ -4224,7 +4230,9 @@
      * position.  By default this is false, not saving the text.  Set to true
      * if the text in the text view is not being saved somewhere else in
      * persistent storage (such as in a content provider) so that if the
-     * view is later thawed the user will not lose their data.
+     * view is later thawed the user will not lose their data. For
+     * {@link android.widget.EditText} it is always enabled, regardless of
+     * the value of the attribute.
      *
      * @param freezesText Controls whether a frozen icicle should include the
      * entire text data: true to include it, false to not.
@@ -4238,7 +4246,7 @@
 
     /**
      * Return whether this text view is including its entire text contents
-     * in frozen icicles.
+     * in frozen icicles. For {@link android.widget.EditText} it always returns true.
      *
      * @return Returns true if text is included, false if it isn't.
      *
@@ -5452,15 +5460,9 @@
         return (int) Math.max(0, mShadowDy + mShadowRadius);
     }
 
-    private int getFudgedPaddingRight() {
-        // Add sufficient space for cursor and tone marks
-        int cursorWidth = 2 + (int)mTextPaint.density; // adequate for Material cursors
-        return Math.max(0, getCompoundPaddingRight() - (cursorWidth - 1));
-    }
-
     @Override
     protected int getRightPaddingOffset() {
-        return -(getFudgedPaddingRight() - mPaddingRight) +
+        return -(getCompoundPaddingRight() - mPaddingRight) +
                 (int) Math.max(0, mShadowDx + mShadowRadius);
     }
 
@@ -5805,7 +5807,7 @@
 
         float clipLeft = compoundPaddingLeft + scrollX;
         float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
-        float clipRight = right - left - getFudgedPaddingRight() + scrollX;
+        float clipRight = right - left - getCompoundPaddingRight() + scrollX;
         float clipBottom = bottom - top + scrollY -
                 ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
 
@@ -10111,8 +10113,8 @@
      * {@link View#onSaveInstanceState}.
      */
     public static class SavedState extends BaseSavedState {
-        int selStart;
-        int selEnd;
+        int selStart = -1;
+        int selEnd = -1;
         CharSequence text;
         boolean frozenWithFocus;
         CharSequence error;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index d4ada95..b2ae835 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1706,7 +1706,9 @@
             mDecorCaptionView.addView(root,
                     new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
         } else {
-            addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+            // Put it below the color views.
+            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         }
         mContentRoot = (ViewGroup) root;
         initializeElevation();
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4cddb6c..1db75e6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -968,8 +968,7 @@
 
     <!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
          to record or display them to the user.
-         <p>Not for use by third-party applications.
-         @hide Pending API council approval -->
+         <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
         android:protectionLevel="signature|privileged" />
 
@@ -2884,6 +2883,18 @@
     <permission android:name="android.permission.DISPATCH_PROVISIONING_MESSAGE"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows the holder to read blocked numbers. See
+         {@link android.provider.BlockedNumberContract}.
+         @hide -->
+    <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+                android:protectionLevel="signature" />
+
+    <!-- Allows the holder to write blocked numbers. See
+         {@link android.provider.BlockedNumberContract}.
+         @hide -->
+    <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
+                android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/values-mcc310-mnc200-tr/strings.xml b/core/res/res/values-mcc310-mnc200-tr/strings.xml
index 72da175..e104eb9 100644
--- a/core/res/res/values-mcc310-mnc200-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc200-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="9107329079910661798">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="9107329079910661798">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="2841003137832065541">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
index 30fdaa4..61bd0a1 100644
--- a/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="2841003137832065541">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc210-tr/strings.xml b/core/res/res/values-mcc310-mnc210-tr/strings.xml
index 3dfa134..1da0b1f 100644
--- a/core/res/res/values-mcc310-mnc210-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc210-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="5217754856196352581">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="5217754856196352581">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4688475512286389971">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
index 5ab2233..f890edf 100644
--- a/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4688475512286389971">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc220-tr/strings.xml b/core/res/res/values-mcc310-mnc220-tr/strings.xml
index 573982e..47aa7b7 100644
--- a/core/res/res/values-mcc310-mnc220-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc220-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="6238990105876016549">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="6238990105876016549">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="2866631708941520085">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
index 36852ad..a846198 100644
--- a/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc220-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="6238990105876016549">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="6238990105876016549">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="2866631708941520085">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc230-tr/strings.xml b/core/res/res/values-mcc310-mnc230-tr/strings.xml
index 6f27685..a659f93 100644
--- a/core/res/res/values-mcc310-mnc230-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc230-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="9007462326786949889">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="9007462326786949889">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="6747587721329739803">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
index a672a77..784f518 100644
--- a/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc230-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="9007462326786949889">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="9007462326786949889">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="6747587721329739803">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc240-tr/strings.xml b/core/res/res/values-mcc310-mnc240-tr/strings.xml
index 3917e99..3386699 100644
--- a/core/res/res/values-mcc310-mnc240-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc240-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="2734345662112241986">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="2734345662112241986">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5561711399459051107">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
index e35f1a9..e5c299c 100644
--- a/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc240-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="2734345662112241986">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="2734345662112241986">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5561711399459051107">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc250-tr/strings.xml b/core/res/res/values-mcc310-mnc250-tr/strings.xml
index b44d969..a62afaf 100644
--- a/core/res/res/values-mcc310-mnc250-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc250-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="3177110876268966">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="3177110876268966">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5743977848030289234">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
index ccb3c39..b90c30a 100644
--- a/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc250-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="3177110876268966">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="3177110876268966">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5743977848030289234">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc270-tr/strings.xml b/core/res/res/values-mcc310-mnc270-tr/strings.xml
index 2faa740..4a2fc4c 100644
--- a/core/res/res/values-mcc310-mnc270-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc270-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="6674750523418536585">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="6674750523418536585">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5880767641285399402">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
index 7313d53..ec0e510 100644
--- a/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc270-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="6674750523418536585">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="6674750523418536585">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5880767641285399402">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc310-tr/strings.xml b/core/res/res/values-mcc310-mnc310-tr/strings.xml
index 12d2864..b0389f1 100644
--- a/core/res/res/values-mcc310-mnc310-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc310-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="1972026366984640493">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="1972026366984640493">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="1383416528714661108">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
index 99fe393..16fa1a4 100644
--- a/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc310-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="1972026366984640493">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="1972026366984640493">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="1383416528714661108">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc490-tr/strings.xml b/core/res/res/values-mcc310-mnc490-tr/strings.xml
index 71ba5bb..2c44a4c 100644
--- a/core/res/res/values-mcc310-mnc490-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc490-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="2780619740658228275">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="2780619740658228275">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4633656294483906293">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
index 1d39868..9348b30 100644
--- a/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc490-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="2780619740658228275">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="2780619740658228275">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4633656294483906293">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc660-tr/strings.xml b/core/res/res/values-mcc310-mnc660-tr/strings.xml
index 7e02f95..aa8bc99 100644
--- a/core/res/res/values-mcc310-mnc660-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc660-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="4027376374798357928">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="4027376374798357928">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5536938168415300276">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
index 2a217b5..20d8703 100644
--- a/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc660-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="4027376374798357928">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="4027376374798357928">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="5536938168415300276">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-mcc310-mnc800-tr/strings.xml b/core/res/res/values-mcc310-mnc800-tr/strings.xml
index 735ce58..9e9d8e7 100644
--- a/core/res/res/values-mcc310-mnc800-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc800-tr/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="8435554129271297367">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+    <item msgid="8435554129271297367">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="8993797655078232716">"Operatörünüze kaydolun"</item>
diff --git a/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
index 0bd3c7a..ff4215d 100644
--- a/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc800-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="8435554129271297367">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。"</item>
+    <item msgid="8435554129271297367">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="8993797655078232716">"向您的流動網絡供應商註冊"</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 482378a..1680259 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1031,7 +1031,7 @@
     <string name="sim_added_title" msgid="3719670512889674693">"SIM 卡已新增"</string>
     <string name="sim_added_message" msgid="7797975656153714319">"重新啟動裝置,才能使用流動網絡。"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"重新啟動"</string>
-    <string name="carrier_app_dialog_message" msgid="7066156088266319533">"為了讓新的 SIM 卡正常運作,您必須先安裝並開啟流動網絡供應商提供的應用程式。"</string>
+    <string name="carrier_app_dialog_message" msgid="7066156088266319533">"為確保新的 SIM 卡正常運作,您必須先安裝並開啟流動網絡供應商提供的應用程式。"</string>
     <string name="carrier_app_dialog_button" msgid="7900235513678617329">"下載應用程式"</string>
     <string name="carrier_app_dialog_not_now" msgid="6361378684292268027">"暫時不要"</string>
     <string name="carrier_app_notification_title" msgid="8921767385872554621">"已插入新的 SIM 卡"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b9d8661..be8577a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4430,7 +4430,9 @@
              inside of its frozen icicle in addition to meta-data such as
              the current cursor position.  By default this is disabled;
              it can be useful when the contents of a text view is not stored
-             in a persistent place such as a content provider. -->
+             in a persistent place such as a content provider. For
+             {@link android.widget.EditText} it is always enabled, regardless
+             of the value of the attribute. -->
         <attr name="freezesText" format="boolean" />
         <!-- If set, causes words that are longer than the view is wide
              to be ellipsized instead of broken in the middle.
diff --git a/core/tests/coretests/res/layout/animator_set_squares.xml b/core/tests/coretests/res/layout/animator_set_squares.xml
index 23e6eea..6888248f 100644
--- a/core/tests/coretests/res/layout/animator_set_squares.xml
+++ b/core/tests/coretests/res/layout/animator_set_squares.xml
@@ -4,7 +4,8 @@
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:background="@android:color/white"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:id="@+id/container">
     <View
         android:layout_width="50dp"
         android:layout_height="50dp"
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
new file mode 100644
index 0000000..dc60423
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.transition.Transition.TransitionListener;
+import android.transition.Transition.TransitionListenerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+
+public class FadeTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+    Activity mActivity;
+    public FadeTransitionTest() {
+        super(AnimatorSetActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mActivity = getActivity();
+    }
+
+    @SmallTest
+    public void testFadeOutAndIn() throws Throwable {
+        View square1 = mActivity.findViewById(R.id.square1);
+        Fade fadeOut = new Fade(Fade.MODE_OUT);
+        TransitionLatch latch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        assertFalse(square1.getTransitionAlpha() == 0 || square1.getTransitionAlpha() == 1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(1.0f, square1.getTransitionAlpha());
+        assertEquals(View.INVISIBLE, square1.getVisibility());
+
+        Fade fadeIn = new Fade(Fade.MODE_IN);
+        latch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        final float transitionAlpha = square1.getTransitionAlpha();
+        assertTrue("expecting transitionAlpha to be between 0 and 1. Was " + transitionAlpha,
+                transitionAlpha > 0 && transitionAlpha < 1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(1.0f, square1.getTransitionAlpha());
+        assertEquals(View.VISIBLE, square1.getVisibility());
+    }
+
+    @SmallTest
+    public void testFadeOutInterrupt() throws Throwable {
+        View square1 = mActivity.findViewById(R.id.square1);
+        Fade fadeOut = new Fade(Fade.MODE_OUT);
+        FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+        fadeOut.addListener(fadeOutValueCheck);
+        TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+        assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        Thread.sleep(100);
+
+        Fade fadeIn = new Fade(Fade.MODE_IN);
+        FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+        fadeIn.addListener(fadeInValueCheck);
+        TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+        assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+        assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+        assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+                fadeOutValueCheck.pauseTransitionAlpha,
+                fadeOutValueCheck.pauseTransitionAlpha > 0 &&
+                        fadeOutValueCheck.pauseTransitionAlpha < 1);
+
+        assertTrue(inLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(1.0f, square1.getTransitionAlpha());
+        assertEquals(View.VISIBLE, square1.getVisibility());
+    }
+
+    @SmallTest
+    public void testFadeInInterrupt() throws Throwable {
+        final View square1 = mActivity.findViewById(R.id.square1);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                square1.setVisibility(View.INVISIBLE);
+            }
+        });
+        Fade fadeIn = new Fade(Fade.MODE_IN);
+        FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+        fadeIn.addListener(fadeInValueCheck);
+        TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+        assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        Thread.sleep(100);
+
+        Fade fadeOut = new Fade(Fade.MODE_OUT);
+        FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+        fadeOut.addListener(fadeOutValueCheck);
+        TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+        assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+        assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+        assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+                        fadeInValueCheck.pauseTransitionAlpha,
+                fadeInValueCheck.pauseTransitionAlpha > 0 &&
+                        fadeInValueCheck.pauseTransitionAlpha < 1);
+
+        assertTrue(outLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(1.0f, square1.getTransitionAlpha());
+        assertEquals(View.INVISIBLE, square1.getVisibility());
+    }
+
+    public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+            final int visibility) throws Throwable {
+        final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+        final View view = sceneRoot.findViewById(viewId);
+        TransitionLatch latch = new TransitionLatch();
+        transition.addListener(latch);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(sceneRoot, transition);
+                view.setVisibility(visibility);
+            }
+        });
+        return latch;
+    }
+
+    public static class TransitionLatch implements TransitionListener {
+        public CountDownLatch startLatch = new CountDownLatch(1);
+        public CountDownLatch endLatch = new CountDownLatch(1);
+        public CountDownLatch cancelLatch = new CountDownLatch(1);
+        public CountDownLatch pauseLatch = new CountDownLatch(1);
+        public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+        @Override
+        public void onTransitionStart(Transition transition) {
+            startLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionEnd(Transition transition) {
+            endLatch.countDown();
+            transition.removeListener(this);
+        }
+
+        @Override
+        public void onTransitionCancel(Transition transition) {
+            cancelLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionPause(Transition transition) {
+            pauseLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionResume(Transition transition) {
+            resumeLatch.countDown();
+        }
+    }
+
+    private static class FadeValueCheck extends TransitionListenerAdapter {
+        public float startTransitionAlpha;
+        public float pauseTransitionAlpha;
+        private final View mView;
+
+        public FadeValueCheck(View view) {
+            mView = view;
+        }
+        @Override
+        public void onTransitionStart(Transition transition) {
+            startTransitionAlpha = mView.getTransitionAlpha();
+        }
+
+        @Override
+        public void onTransitionPause(Transition transition) {
+            pauseTransitionAlpha = mView.getTransitionAlpha();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
new file mode 100644
index 0000000..04c8b8c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ViewGroup;
+
+public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
+
+    private EditText mEditText;
+    private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";
+
+    public EditorCursorTest() {
+        super(TextViewActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mEditText = new EditText(getActivity());
+        mEditText.setTextSize(30);
+        mEditText.setSingleLine(true);
+        mEditText.setLines(1);
+        mEditText.setPadding(15, 15, 15, 15);
+        ViewGroup.LayoutParams editTextLayoutParams = new ViewGroup.LayoutParams(200,
+                ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        mEditText.setLayoutParams(editTextLayoutParams);
+
+        final FrameLayout layout = new FrameLayout(getActivity());
+        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT);
+        layout.setLayoutParams(layoutParams);
+        layout.addView(mEditText);
+
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().setContentView(layout);
+                mEditText.requestFocus();
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+    }
+
+    @SmallTest
+    public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception {
+        // Asserts that when an EditText has LTR text, and cursor is at the end (right),
+        // cursor is drawn to the right edge of the view
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
+                int length = mEditText.getText().length();
+                mEditText.setSelection(length, length);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        Editor editor = mEditText.getEditorForTesting();
+        Drawable drawable = editor.getCursorDrawable()[0];
+        Rect drawableBounds = drawable.getBounds();
+        Rect drawablePadding = new Rect();
+        drawable.getPadding(drawablePadding);
+
+        // right edge of the view including the scroll
+        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
+                - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX();
+        int diff = drawableBounds.right - drawablePadding.right - maxRight;
+        assertTrue(diff >= 0 && diff <= 1);
+    }
+
+    @SmallTest
+    public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception {
+        // Asserts that when an EditText has LTR text, and cursor is at the beginning,
+        // cursor is drawn to the left edge of the view
+
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
+                mEditText.setSelection(0, 0);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+        Rect drawableBounds = drawable.getBounds();
+        Rect drawablePadding = new Rect();
+        drawable.getPadding(drawablePadding);
+
+        int diff = drawableBounds.left + drawablePadding.left;
+        assertTrue(diff >= 0 && diff <= 1);
+    }
+
+    @SmallTest
+    public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception {
+        // Asserts that when an EditText has RTL text, and cursor is at the end,
+        // cursor is drawn to the left edge of the view
+
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mEditText.setText(RTL_STRING);
+                mEditText.setSelection(0, 0);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+        Rect drawableBounds = drawable.getBounds();
+        Rect drawablePadding = new Rect();
+        drawable.getPadding(drawablePadding);
+
+        int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
+                - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX();
+
+        int diff = drawableBounds.right - drawablePadding.right - maxRight;
+        assertTrue(diff >= 0 && diff <= 1);
+    }
+
+    @SmallTest
+    public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception {
+        // Asserts that when an EditText has RTL text, and cursor is at the beginning,
+        // cursor is drawn to the right edge of the view
+
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mEditText.setText(RTL_STRING);
+                int length = mEditText.getText().length();
+                mEditText.setSelection(length, length);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+        Rect drawableBounds = drawable.getBounds();
+        Rect drawablePadding = new Rect();
+        drawable.getPadding(drawablePadding);
+
+        int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left;
+        assertTrue(diff >= 0 && diff <= 1);
+    }
+
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 7b43947..6bd039c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -324,4 +324,6 @@
         tests/microbench/FrameBuilderBench.cpp
 endif
 
+LOCAL_CLANG := true # workaround gcc bug
+
 include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index e3a5f3e..f83e1fa 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -201,8 +201,7 @@
 
     renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
     ShadowTexture* texture = renderer.caches().dropShadowCache.get(
-            op.paint, (const char*) op.glyphs,
-            op.glyphCount, textShadow.radius, op.positions);
+            op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
     // If the drop shadow exceeds the max texture size or couldn't be
     // allocated, skip drawing
     if (!texture) return;
@@ -277,8 +276,7 @@
     bool forceFinish = (renderType == TextRenderType::Flush);
     bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
     const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
-    fontRenderer.renderPosText(op.paint, localOpClip,
-            (const char*) op.glyphs, op.glyphCount, x, y,
+    fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
             op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
 
     if (mustDirtyRenderTarget) {
@@ -701,8 +699,7 @@
 
     bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
     const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
-    if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
-            reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
+    if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
             op.path, op.hOffset, op.vOffset,
             mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
         if (mustDirtyRenderTarget) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 3db14b5..00560d7 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -428,7 +428,7 @@
     if (!glyphs || count <= 0) return;
 
     int bytesCount = 2 * count;
-    DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount),
+    DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer<glyph_t>(glyphs, count),
             bytesCount, count, refPath(&path),
             hOffset, vOffset, refPaint(&paint));
     addDrawOp(op);
@@ -442,11 +442,10 @@
     if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
 
     int bytesCount = count * 2;
-    const char* text = refText((const char*) glyphs, bytesCount);
     positions = refBuffer<float>(positions, count * 2);
     Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
 
-    DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+    DrawOp* op = new (alloc()) DrawTextOp(refBuffer<glyph_t>(glyphs, count), bytesCount, count,
             x, y, positions, refPaint(&paint), totalAdvance, bounds);
     addDrawOp(op);
     drawTextDecorations(x, y, totalAdvance, paint);
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 06e72a0..e5711e3 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -256,10 +256,6 @@
         return dstBuffer;
     }
 
-    inline char* refText(const char* text, size_t byteLength) {
-        return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
-    }
-
     inline const SkPath* refPath(const SkPath* path) {
         if (!path) return nullptr;
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 92217edc..20501ba 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1229,7 +1229,7 @@
 
 class DrawSomeTextOp : public DrawOp {
 public:
-    DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint)
+    DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint)
             : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
 
     virtual void output(int level, uint32_t logFlags) const override {
@@ -1251,14 +1251,14 @@
     }
 
 protected:
-    const char* mText;
+    const glyph_t* mText;
     int mBytesCount;
     int mCount;
 };
 
 class DrawTextOnPathOp : public DrawSomeTextOp {
 public:
-    DrawTextOnPathOp(const char* text, int bytesCount, int count,
+    DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count,
             const SkPath* path, float hOffset, float vOffset, const SkPaint* paint)
             : DrawSomeTextOp(text, bytesCount, count, paint),
             mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
@@ -1280,7 +1280,7 @@
 
 class DrawTextOp : public DrawStrokableOp {
 public:
-    DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
+    DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y,
             const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds)
             : DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
             mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
@@ -1341,7 +1341,7 @@
     virtual const char* name() override { return "DrawText"; }
 
 private:
-    const char* mText;
+    const glyph_t* mText;
     int mBytesCount;
     int mCount;
     float mX;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 68bae6d..1b618c6 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -557,7 +557,7 @@
     mCurrentFont = Font::create(this, paint, matrix);
 }
 
-FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
+FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
         int numGlyphs, float radius, const float* positions) {
     checkInit();
 
@@ -577,7 +577,7 @@
     mBounds = nullptr;
 
     Rect bounds;
-    mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions);
+    mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
 
     uint32_t intRadius = Blur::convertRadiusToInt(radius);
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
@@ -609,7 +609,7 @@
         // text has non-whitespace, so draw and blur to create the shadow
         // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
         // TODO: don't draw pure whitespace in the first place, and avoid needing this check
-        mCurrentFont->render(paint, text, numGlyphs, penX, penY,
+        mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
                 Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
 
         // Unbind any PBO we might have used
@@ -643,17 +643,17 @@
     issueDrawCommand();
 }
 
-void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs,
+void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
         const SkMatrix& matrix) {
     Font* font = Font::create(this, paint, matrix);
-    font->precache(paint, text, numGlyphs);
+    font->precache(paint, glyphs, numGlyphs);
 }
 
 void FontRenderer::endPrecaching() {
     checkTextureUpdate();
 }
 
-bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
         int numGlyphs, int x, int y, const float* positions,
         Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
     if (!mCurrentFont) {
@@ -662,7 +662,7 @@
     }
 
     initRender(clip, bounds, functor);
-    mCurrentFont->render(paint, text, numGlyphs, x, y, positions);
+    mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
 
     if (forceFinish) {
         finishRender();
@@ -671,7 +671,7 @@
     return mDrawn;
 }
 
-bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
         int numGlyphs, const SkPath* path, float hOffset, float vOffset,
         Rect* bounds, TextDrawFunctor* functor) {
     if (!mCurrentFont) {
@@ -680,7 +680,7 @@
     }
 
     initRender(clip, bounds, functor);
-    mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset);
+    mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
     finishRender();
 
     return mDrawn;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9994498..e10a81b 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -104,14 +104,14 @@
 
     void setFont(const SkPaint* paint, const SkMatrix& matrix);
 
-    void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
+    void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix);
     void endPrecaching();
 
-    bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+    bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
             int numGlyphs, int x, int y, const float* positions,
             Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true);
 
-    bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+    bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
             int numGlyphs, const SkPath* path,
             float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor);
 
@@ -125,7 +125,7 @@
 
     // After renderDropShadow returns, the called owns the memory in DropShadow.image
     // and is responsible for releasing it when it's done with it
-    DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs,
+    DropShadow renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs,
             float radius, const float* positions);
 
     void setTextureFiltering(bool linearFiltering) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 587be92..b7a5923 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1949,7 +1949,7 @@
     }
 }
 
-void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
+void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs,
         int count, const float* positions,
         FontRenderer& fontRenderer, int alpha, float x, float y) {
     mCaches.textureState().activateTexture(0);
@@ -1963,7 +1963,7 @@
     //       if shader-based correction is enabled
     mCaches.dropShadowCache.setFontRenderer(fontRenderer);
     ShadowTexture* texture = mCaches.dropShadowCache.get(
-            paint, text, count, textShadow.radius, positions);
+            paint, glyphs, count, textShadow.radius, positions);
     // If the drop shadow exceeds the max texture size or couldn't be
     // allocated, skip drawing
     if (!texture) return;
@@ -2084,14 +2084,14 @@
     mState.setProjectionPathMask(allocator, path);
 }
 
-void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
         const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
         DrawOpMode drawOpMode) {
 
     if (drawOpMode == DrawOpMode::kImmediate) {
         // The checks for corner-case ignorable text and quick rejection is only done for immediate
         // drawing as ops from DeferredDisplayList are already filtered for these
-        if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
+        if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
                 quickRejectSetupScissor(bounds)) {
             return;
         }
@@ -2115,7 +2115,7 @@
 
     if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
         fontRenderer.setFont(paint, SkMatrix::I());
-        drawTextShadow(paint, text, count, positions, fontRenderer,
+        drawTextShadow(paint, glyphs, count, positions, fontRenderer,
                 alpha, oldX, oldY);
     }
 
@@ -2156,10 +2156,10 @@
     if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
         SkPaint paintCopy(*paint);
         paintCopy.setTextAlign(SkPaint::kLeft_Align);
-        status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y,
+        status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y,
                 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
     } else {
-        status = fontRenderer.renderPosText(paint, clip, text, count, x, y,
+        status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y,
                 positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
     }
 
@@ -2173,9 +2173,9 @@
     mDirty = true;
 }
 
-void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count,
         const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
-    if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
+    if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
         return;
     }
 
@@ -2198,7 +2198,7 @@
     const Rect* clip = &writableSnapshot()->getLocalClip();
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
-    if (fontRenderer.renderTextOnPath(paint, clip, text, count, path,
+    if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path,
             hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
         dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
         mDirty = true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 84bc9b0..dacd8cc 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -191,9 +191,9 @@
     void drawPath(const SkPath* path, const SkPaint* paint);
     void drawLines(const float* points, int count, const SkPaint* paint);
     void drawPoints(const float* points, int count, const SkPaint* paint);
-    void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+    void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path,
             float hOffset, float vOffset, const SkPaint* paint);
-    void drawText(const char* text, int bytesCount, int count, float x, float y,
+    void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
             const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
             DrawOpMode drawOpMode = DrawOpMode::kImmediate);
     void drawRects(const float* rects, int count, const SkPaint* paint);
@@ -647,7 +647,7 @@
      * @param x The x coordinate where the shadow will be drawn
      * @param y The y coordinate where the shadow will be drawn
      */
-    void drawTextShadow(const SkPaint* paint, const char* text, int count,
+    void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count,
             const float* positions, FontRenderer& fontRenderer, int alpha,
             float x, float y);
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3e11151..249b5b0 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -31,9 +31,6 @@
 // Compile-time properties
 ///////////////////////////////////////////////////////////////////////////////
 
-// If turned on, text is interpreted as glyphs instead of UTF-16
-#define RENDER_TEXT_AS_GLYPHS 1
-
 // Textures used by layers must have dimensions multiples of this number
 #define LAYER_SIZE 64
 
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index fe4b3d75..e1f0b2a 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -37,9 +37,9 @@
     hash = JenkinsHashMix(hash, flags);
     hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
     hash = JenkinsHashMix(hash, android::hash_type(scaleX));
-    if (text) {
+    if (glyphs) {
         hash = JenkinsHashMixShorts(
-            hash, reinterpret_cast<const uint16_t*>(text), glyphCount);
+            hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
     }
     if (positions) {
         for (uint32_t i = 0; i < glyphCount * 2; i++) {
@@ -71,11 +71,11 @@
     if (lhs.scaleX < rhs.scaleX) return -1;
     if (lhs.scaleX > rhs.scaleX) return +1;
 
-    if (lhs.text != rhs.text) {
-        if (!lhs.text) return -1;
-        if (!rhs.text) return +1;
+    if (lhs.glyphs != rhs.glyphs) {
+        if (!lhs.glyphs) return -1;
+        if (!rhs.glyphs) return +1;
 
-        deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t));
+        deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
         if (deltaInt != 0) return deltaInt;
     }
 
@@ -145,7 +145,7 @@
     mCache.clear();
 }
 
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs,
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
         float radius, const float* positions) {
     ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
     ShadowTexture* texture = mCache.get(entry);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index cf64788..d536c40 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,26 +35,21 @@
 
 struct ShadowText {
     ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
-            flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
+            flags(0), italicStyle(0.0f), scaleX(0), glyphs(nullptr), positions(nullptr) {
     }
 
     // len is the number of bytes in text
-    ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText,
-            const float* positions):
-            glyphCount(glyphCount), radius(radius), positions(positions) {
-        // TODO: Propagate this through the API, we should not cast here
-        text = (const char16_t*) srcText;
-
-        textSize = paint->getTextSize();
-        typeface = paint->getTypeface();
-
-        flags = 0;
-        if (paint->isFakeBoldText()) {
-            flags |= Font::kFakeBold;
-        }
-
-        italicStyle = paint->getTextSkewX();
-        scaleX = paint->getTextScaleX();
+    ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs,
+            const float* positions)
+            : glyphCount(glyphCount)
+            , radius(radius)
+            , textSize(paint->getTextSize())
+            , typeface(paint->getTypeface())
+            , flags(paint->isFakeBoldText() ? Font::kFakeBold : 0)
+            , italicStyle(paint->getTextSkewX())
+            , scaleX(paint->getTextScaleX())
+            , glyphs(srcGlyphs)
+            , positions(positions) {
     }
 
     ~ShadowText() {
@@ -73,8 +68,8 @@
     }
 
     void copyTextLocally() {
-        str.setTo((const char16_t*) text, glyphCount);
-        text = str.string();
+        str.setTo(reinterpret_cast<const char16_t*>(glyphs), glyphCount);
+        glyphs = reinterpret_cast<const glyph_t*>(str.string());
         if (positions != nullptr) {
             positionsCopy.clear();
             positionsCopy.appendArray(positions, glyphCount * 2);
@@ -89,7 +84,7 @@
     uint32_t flags;
     float italicStyle;
     float scaleX;
-    const char16_t* text;
+    const glyph_t* glyphs;
     const float* positions;
 
     // Not directly used to compute the cache key
@@ -135,7 +130,7 @@
      */
     void operator()(ShadowText& text, ShadowTexture*& texture) override;
 
-    ShadowTexture* get(const SkPaint* paint, const char* text,
+    ShadowTexture* get(const SkPaint* paint, const glyph_t* text,
             int numGlyphs, float radius, const float* positions);
 
     /**
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index dc82041..9a825fd 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -291,15 +291,15 @@
     return cachedGlyph;
 }
 
-void Font::render(const SkPaint* paint, const char *text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
             int numGlyphs, int x, int y, const float* positions) {
-    render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+    render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr,
             0, 0, nullptr, positions);
 }
 
-void Font::render(const SkPaint* paint, const char *text, int numGlyphs,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
         const SkPath* path, float hOffset, float vOffset) {
-    if (numGlyphs == 0 || text == nullptr) {
+    if (numGlyphs == 0 || glyphs == nullptr) {
         return;
     }
 
@@ -315,7 +315,7 @@
     float pathLength = SkScalarToFloat(measure.getLength());
 
     if (paint->getTextAlign() != SkPaint::kLeft_Align) {
-        float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2));
+        float textWidth = SkScalarToFloat(paint->measureText(glyphs, numGlyphs * 2));
         float pathOffset = pathLength;
         if (paint->getTextAlign() == SkPaint::kCenter_Align) {
             textWidth *= 0.5f;
@@ -325,7 +325,7 @@
     }
 
     while (glyphsCount < numGlyphs && penX < pathLength) {
-        glyph_t glyph = GET_GLYPH(text);
+        glyph_t glyph = *(glyphs++);
 
         if (IS_END_OF_STRING(glyph)) {
             break;
@@ -345,26 +345,26 @@
     }
 }
 
-void Font::measure(const SkPaint* paint, const char* text,
+void Font::measure(const SkPaint* paint, const glyph_t* glyphs,
         int numGlyphs, Rect *bounds, const float* positions) {
     if (bounds == nullptr) {
         ALOGE("No return rectangle provided to measure text");
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
+    render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
 }
 
-void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
+void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
     ATRACE_NAME("Precache Glyphs");
 
-    if (numGlyphs == 0 || text == nullptr) {
+    if (numGlyphs == 0 || glyphs == nullptr) {
         return;
     }
 
     int glyphsCount = 0;
     while (glyphsCount < numGlyphs) {
-        glyph_t glyph = GET_GLYPH(text);
+        glyph_t glyph = *(glyphs++);
 
         // Reached the end of the string
         if (IS_END_OF_STRING(glyph)) {
@@ -376,10 +376,10 @@
     }
 }
 
-void Font::render(const SkPaint* paint, const char* text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
         int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
         uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
-    if (numGlyphs == 0 || text == nullptr) {
+    if (numGlyphs == 0 || glyphs == nullptr) {
         return;
     }
 
@@ -396,7 +396,7 @@
     int glyphsCount = 0;
 
     while (glyphsCount < numGlyphs) {
-        glyph_t glyph = GET_GLYPH(text);
+        glyph_t glyph = *(glyphs++);
 
         // Reached the end of the string
         if (IS_END_OF_STRING(glyph)) {
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 59518a1..e8882d9 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -82,10 +82,10 @@
 
     ~Font();
 
-    void render(const SkPaint* paint, const char* text,
+    void render(const SkPaint* paint, const glyph_t* glyphs,
             int numGlyphs, int x, int y, const float* positions);
 
-    void render(const SkPaint* paint, const char* text,
+    void render(const SkPaint* paint, const glyph_t* glyphs,
             int numGlyphs, const SkPath* path, float hOffset, float vOffset);
 
     const Font::FontDescription& getDescription() const {
@@ -111,13 +111,13 @@
         MEASURE,
     };
 
-    void precache(const SkPaint* paint, const char* text, int numGlyphs);
+    void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs);
 
-    void render(const SkPaint* paint, const char *text,
+    void render(const SkPaint* paint, const glyph_t* glyphs,
             int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
             uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
 
-    void measure(const SkPaint* paint, const char* text,
+    void measure(const SkPaint* paint, const glyph_t* glyphs,
             int numGlyphs, Rect *bounds, const float* positions);
 
     void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index 4e5debe..aa77d98 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -40,26 +40,9 @@
 
 #define CACHE_BLOCK_ROUNDING_SIZE 4
 
-#if RENDER_TEXT_AS_GLYPHS
-    typedef uint16_t glyph_t;
-    #define TO_GLYPH(g) g
-    #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
-    #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
-    #define IS_END_OF_STRING(glyph) false
-
-    static inline glyph_t nextGlyph(const uint16_t** srcPtr) {
-        const uint16_t* src = *srcPtr;
-        glyph_t g = *src++;
-        *srcPtr = src;
-        return g;
-    }
-#else
-    typedef SkUnichar glyph_t;
-    #define TO_GLYPH(g) ((SkUnichar) g)
-    #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph)
-    #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
-    #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
+typedef uint16_t glyph_t;
+#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
+#define IS_END_OF_STRING(glyph) false
 
 #define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
 
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5ed7aa4..3440d03 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -58,27 +58,22 @@
     return layerUpdater;
 }
 
-void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
-        const SkPaint& paint, float x, float y) {
-    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
-    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
-            "must use glyph encoding");
+void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
+        std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+        float* outTotalAdvance, Rect* outBounds) {
+    Rect bounds;
+    float totalAdvance = 0;
     SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
     SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
-    float totalAdvance = 0;
-    std::vector<glyph_t> glyphs;
-    std::vector<float> positions;
-    Rect bounds;
     while (*text != '\0') {
         SkUnichar unichar = SkUTF8_NextUnichar(&text);
         glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
         autoCache.getCache()->unicharToGlyph(unichar);
 
         // push glyph and its relative position
-        glyphs.push_back(glyph);
-        positions.push_back(totalAdvance);
-        positions.push_back(0);
+        outGlyphs->push_back(glyph);
+        outPositions->push_back(totalAdvance);
+        outPositions->push_back(0);
 
         // compute bounds
         SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
@@ -91,6 +86,23 @@
         paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
         totalAdvance += skWidth;
     }
+    *outBounds = bounds;
+    *outTotalAdvance = totalAdvance;
+}
+
+void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+        const SkPaint& paint, float x, float y) {
+    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
+    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
+            "must use glyph encoding");
+    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
+
+    std::vector<glyph_t> glyphs;
+    std::vector<float> positions;
+    float totalAdvance;
+    Rect bounds;
+    layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
 
     // apply alignment via x parameter (which JNI layer would have done)
     if (paint.getTextAlign() == SkPaint::kCenter_Align) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ae08142..6f23705 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -205,6 +205,10 @@
 
     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
 
+    static void layoutTextUnscaled(const SkPaint& paint, const char* text,
+            std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+            float* outTotalAdvance, Rect* outBounds);
+
     static void drawTextToCanvas(TestCanvas* canvas, const char* text,
             const SkPaint& paint, float x, float y);
 
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index c54f2c3..0d26df2 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -21,29 +21,31 @@
 #include "utils/Blur.h"
 #include "tests/common/TestUtils.h"
 
-#include <SkBlurDrawLooper.h>
 #include <SkPaint.h>
 
 using namespace android;
 using namespace android::uirenderer;
 
 RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+    SkPaint paint;
+    paint.setTextSize(20);
+
     GammaFontRenderer gammaFontRenderer;
     FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
-    TextDropShadowCache cache(5000);
+    fontRenderer.setFont(&paint, SkMatrix::I());
+    TextDropShadowCache cache(MB(5));
     cache.setFontRenderer(fontRenderer);
 
-    SkPaint paint;
-    paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF,
-            Blur::convertRadiusToSigma(10), 10, 10))->unref();
-    std::string msg("This is a test");
-    std::unique_ptr<float[]> positions(new float[msg.length()]);
-    for (size_t i = 0; i < msg.length(); i++) {
-        positions[i] = i * 10.0f;
-    }
-    fontRenderer.setFont(&paint, SkMatrix::I());
-    ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(),
-            10.0f, positions.get());
+    std::vector<glyph_t> glyphs;
+    std::vector<float> positions;
+    float totalAdvance;
+    uirenderer::Rect bounds;
+    TestUtils::layoutTextUnscaled(paint, "This is a test",
+            &glyphs, &positions, &totalAdvance, &bounds);
+    EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized";
+
+    ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data());
+
     ASSERT_TRUE(texture);
     ASSERT_FALSE(texture->cleanup);
     ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize());
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index c194711..a3bbdfc 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -242,6 +243,7 @@
     private int mFlags = 0x0;
     private HashSet<String> mTags;
     private String mFormattedTags;
+    private Bundle mBundle; // lazy-initialized, may be null
 
     private AudioAttributes() {
     }
@@ -295,6 +297,20 @@
 
     /**
      * @hide
+     * Return the Bundle of data.
+     * @return a copy of the Bundle for this instance, may be null.
+     */
+    @SystemApi
+    public Bundle getBundle() {
+        if (mBundle == null) {
+            return mBundle;
+        } else {
+            return new Bundle(mBundle);
+        }
+    }
+
+    /**
+     * @hide
      * Return the set of tags.
      * @return a read-only set of all tags stored as strings.
      */
@@ -327,6 +343,7 @@
         private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
         private int mFlags = 0x0;
         private HashSet<String> mTags = new HashSet<String>();
+        private Bundle mBundle;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -365,6 +382,9 @@
             aa.mFlags = mFlags;
             aa.mTags = (HashSet<String>) mTags.clone();
             aa.mFormattedTags = TextUtils.join(";", mTags);
+            if (mBundle != null) {
+                aa.mBundle = new Bundle(mBundle);
+            }
             return aa;
         }
 
@@ -453,6 +473,25 @@
 
         /**
          * @hide
+         * Adds a Bundle of data
+         * @param bundle a non-null Bundle
+         * @return the same builder instance
+         */
+        @SystemApi
+        public Builder addBundle(@NonNull Bundle bundle) {
+            if (bundle == null) {
+                throw new IllegalArgumentException("Illegal null bundle");
+            }
+            if (mBundle == null) {
+                mBundle = new Bundle(bundle);
+            } else {
+                mBundle.putAll(bundle);
+            }
+            return this;
+        }
+
+        /**
+         * @hide
          * Add a custom tag stored as a string
          * @param tag
          * @return the same Builder instance.
@@ -584,6 +623,10 @@
      * see definition of kAudioAttributesMarshallTagFlattenTags
      */
     public final static int FLATTEN_TAGS = 0x1;
+
+    private final static int ATTR_PARCEL_IS_NULL_BUNDLE = -1977;
+    private final static int ATTR_PARCEL_IS_VALID_BUNDLE = 1980;
+
     /**
      * When adding tags for writeToParcel(Parcel, int), add them in the list of flags (| NEW_FLAG)
      */
@@ -602,6 +645,12 @@
         } else if ((flags & FLATTEN_TAGS) == FLATTEN_TAGS) {
             dest.writeString(mFormattedTags);
         }
+        if (mBundle == null) {
+            dest.writeInt(ATTR_PARCEL_IS_NULL_BUNDLE);
+        } else {
+            dest.writeInt(ATTR_PARCEL_IS_VALID_BUNDLE);
+            dest.writeBundle(mBundle);
+        }
     }
 
     private AudioAttributes(Parcel in) {
@@ -621,6 +670,16 @@
             }
             mFormattedTags = TextUtils.join(";", mTags);
         }
+        switch (in.readInt()) {
+            case ATTR_PARCEL_IS_NULL_BUNDLE:
+                mBundle = null;
+                break;
+            case ATTR_PARCEL_IS_VALID_BUNDLE:
+                mBundle = new Bundle(in.readBundle());
+                break;
+            default:
+                Log.e(TAG, "Illegal value unmarshalling AudioAttributes, can't initialize bundle");
+        }
     }
 
     public static final Parcelable.Creator<AudioAttributes> CREATOR
@@ -655,7 +714,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mContentType, mFlags, mSource, mUsage, mFormattedTags);
+        return Objects.hash(mContentType, mFlags, mSource, mUsage, mFormattedTags, mBundle);
     }
 
     @Override
@@ -664,7 +723,8 @@
                 + " usage=" + mUsage
                 + " content=" + mContentType
                 + " flags=0x" + Integer.toHexString(mFlags).toUpperCase()
-                + " tags=" + mFormattedTags);
+                + " tags=" + mFormattedTags
+                + " bundle=" + (mBundle == null ? "null" : mBundle.toString()));
     }
 
     /** @hide */
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index f9a23f9..c6dece4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -52,6 +52,7 @@
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -502,7 +503,22 @@
         }
 
         TvInputInfo obj = (TvInputInfo) o;
-        return mId.equals(obj.mId);
+        return TextUtils.equals(mId, obj.mId)
+                && TextUtils.equals(mParentId, obj.mParentId)
+                && Objects.equals(mService, obj.mService)
+                && TextUtils.equals(mSetupActivity, obj.mSetupActivity)
+                && TextUtils.equals(mSettingsActivity, obj.mSettingsActivity)
+                && mType == obj.mType
+                && mTunerCount == obj.mTunerCount
+                && mCanRecord == obj.mCanRecord
+                && mIsHardwareInput == obj.mIsHardwareInput
+                && Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
+                && Objects.equals(mIcon, obj.mIcon)
+                && Objects.equals(mIconUri, obj.mIconUri)
+                && mLabelResId == obj.mLabelResId
+                && TextUtils.equals(mLabel, obj.mLabel)
+                && mIsConnectedToHdmiSwitch == obj.mIsConnectedToHdmiSwitch
+                && Objects.equals(mExtras, obj.mExtras);
     }
 
     @Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 684a1de..2ae3ec62 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -282,12 +282,10 @@
             Log.i(LOG_TAG, "[CALLED] cancel()");
         }
 
-        if (mState == STATE_CANCELING) {
-            return;
-        }
+        mNextCommand = null;
 
         if (mState != STATE_UPDATING) {
-            throw new IllegalStateException("Cannot cancel in state:" + stateToString(mState));
+            return;
         }
 
         mState = STATE_CANCELING;
@@ -568,6 +566,8 @@
                         Log.w(LOG_TAG, "Error while canceling", re);
                     }
                 }
+            } else if (isCanceling()) {
+                // Nothing to do
             } else {
                 canceled();
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index d0cf635..08cd0b6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -320,9 +320,7 @@
                 if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
                     return;
                 }
-                if (mPrintedDocument.isUpdating()) {
-                    mPrintedDocument.cancel();
-                }
+                mPrintedDocument.cancel();
                 setState(STATE_PRINT_CANCELED);
                 doFinish();
             }
@@ -372,12 +370,14 @@
             spooler.updatePrintJobUserConfigurableOptionsNoPersistence(mPrintJob);
 
             switch (mState) {
-                case STATE_PRINT_CONFIRMED: {
-                    spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_QUEUED, null);
-                } break;
-
                 case STATE_PRINT_COMPLETED: {
-                    spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_COMPLETED, null);
+                    if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) {
+                        spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_COMPLETED,
+                                null);
+                    } else {
+                        spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_QUEUED,
+                                null);
+                    }
                 } break;
 
                 case STATE_CREATE_FILE_FAILED: {
@@ -491,6 +491,8 @@
                 requestCreatePdfFileOrFinish();
             } break;
 
+            case STATE_CREATE_FILE_FAILED:
+            case STATE_PRINT_COMPLETED:
             case STATE_PRINT_CANCELED: {
                 doFinish();
             } break;
@@ -528,8 +530,12 @@
                 requestCreatePdfFileOrFinish();
             } break;
 
+            case STATE_CREATE_FILE_FAILED:
+            case STATE_PRINT_COMPLETED:
             case STATE_PRINT_CANCELED: {
                 updateOptionsUi();
+
+                doFinish();
             } break;
 
             default: {
@@ -550,6 +556,12 @@
         mProgressMessageController.cancel();
         ensureErrorUiShown(error, PrintErrorFragment.ACTION_RETRY);
 
+        if (mState == STATE_CREATE_FILE_FAILED
+                || mState == STATE_PRINT_COMPLETED
+                || mState == STATE_PRINT_CANCELED) {
+            doFinish();
+        }
+
         setState(STATE_UPDATE_FAILED);
 
         updateOptionsUi();
@@ -644,7 +656,6 @@
 
     private void onStartCreateDocumentActivityResult(int resultCode, Intent data) {
         if (resultCode == RESULT_OK && data != null) {
-            setState(STATE_PRINT_COMPLETED);
             updateOptionsUi();
             final Uri uri = data.getData();
             // Calling finish here does not invoke lifecycle callbacks but we
@@ -657,6 +668,10 @@
             });
         } else if (resultCode == RESULT_CANCELED) {
             mState = STATE_CONFIGURING;
+
+            // The previous update might have been canceled
+            updateDocument(false);
+
             updateOptionsUi();
         } else {
             setState(STATE_CREATE_FILE_FAILED);
@@ -875,9 +890,9 @@
     }
 
     private static boolean isFinalState(int state) {
-        return state == STATE_PRINT_CONFIRMED
-                || state == STATE_PRINT_CANCELED
-                || state == STATE_PRINT_COMPLETED;
+        return state == STATE_PRINT_CANCELED
+                || state == STATE_PRINT_COMPLETED
+                || state == STATE_CREATE_FILE_FAILED;
     }
 
     private void updateSelectedPagesFromPreview() {
@@ -998,6 +1013,8 @@
     }
 
     private void requestCreatePdfFileOrFinish() {
+        mPrintedDocument.cancel();
+
         if (mCurrentPrinter == mDestinationSpinnerAdapter.getPdfPrinter()) {
             startCreateDocumentActivity();
         } else {
@@ -1113,9 +1130,7 @@
     private void cancelPrint() {
         setState(STATE_PRINT_CANCELED);
         updateOptionsUi();
-        if (mPrintedDocument.isUpdating()) {
-            mPrintedDocument.cancel();
-        }
+        mPrintedDocument.cancel();
         doFinish();
     }
 
@@ -1874,9 +1889,7 @@
     public void onPrinterUnavailable(PrinterInfo printer) {
         if (mCurrentPrinter.getId().equals(printer.getId())) {
             setState(STATE_PRINTER_UNAVAILABLE);
-            if (mPrintedDocument.isUpdating()) {
-                mPrintedDocument.cancel();
-            }
+            mPrintedDocument.cancel();
             ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
                     PrintErrorFragment.ACTION_NONE);
             updateOptionsUi();
@@ -1933,12 +1946,18 @@
                 if (writeToUri != null) {
                     mPrintedDocument.writeContent(getContentResolver(), writeToUri);
                 }
+                setState(STATE_PRINT_COMPLETED);
                 doFinish();
             }
         }).transform();
     }
 
     private void doFinish() {
+        if (mPrintedDocument.isUpdating()) {
+            // The printedDocument will call doFinish() when the current command finishes
+            return;
+        }
+
         if (mPrinterRegistry != null) {
             mPrinterRegistry.setTrackedPrinter(null);
         }
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index e8e607b..c26b549 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -13,14 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label">Shell</string>
 
     <!-- Title of notification indicating a bugreport is being generated. [CHAR LIMIT=50] -->
-    <string name="bugreport_in_progress_title">Bug report is being generated</string>
+    <string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
     <!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
-    <string name="bugreport_finished_title">Bug report captured</string>
+    <string name="bugreport_finished_title">Bug report <xliff:g id="id">#%d</xliff:g> captured</string>
     <!-- Title of notification indicating a bugreport is being updated before it can be shared. [CHAR LIMIT=50] -->
     <string name="bugreport_updating_title">Adding details to the bug report</string>
     <!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
@@ -53,12 +52,12 @@
     <string name="bugreport_screenshot_action">Screenshot</string>
 
     <!-- Toast message sent when the a screenshot for the bug report was taken successfully. -->
-    <string name="bugreport_screenshot_taken">Screenshot taken succesfully.</string>
+    <string name="bugreport_screenshot_taken">Screenshot taken successfully.</string>
     <!-- Toast message sent when the a screenshot for the bug report was not taken due to an error. -->
     <string name="bugreport_screenshot_failed">Screenshot could not be taken.</string>
 
     <!--  Title of the dialog asking for user-defined bug report details like name, title, and description. -->
-    <string name="bugreport_info_dialog_title">Bug report details</string>
+    <string name="bugreport_info_dialog_title">Bug report <xliff:g id="id">#%d</xliff:g> details</string>
 
     <!-- Text of the hint asking for the bug report name, which when set will define a suffix in the
          bug report file names. [CHAR LIMIT=30] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 78197b3..8c555a6 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -438,7 +438,7 @@
                 mContext.getString(R.string.bugreport_screenshot_action),
                 screenshotPendingIntent).build();
 
-        final String title = mContext.getString(R.string.bugreport_in_progress_title);
+        final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
 
         final String name =
                 info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -880,7 +880,7 @@
         shareIntent.putExtra(EXTRA_PID, info.pid);
         shareIntent.putExtra(EXTRA_INFO, info);
 
-        final String title = context.getString(R.string.bugreport_finished_title);
+        final String title = context.getString(R.string.bugreport_finished_title, info.pid);
         final Notification.Builder builder = new Notification.Builder(context)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
                 .setContentTitle(title)
@@ -1232,7 +1232,7 @@
 
                 mDialog = new AlertDialog.Builder(context)
                         .setView(view)
-                        .setTitle(context.getString(R.string.bugreport_info_dialog_title))
+                        .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
                         .setCancelable(false)
                         .setPositiveButton(context.getString(com.android.internal.R.string.ok),
                                 null)
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index ea85c61..8c62670 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -36,6 +36,7 @@
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.SortedSet;
@@ -58,6 +59,7 @@
 import android.service.notification.StatusBarNotification;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.text.TextUtils;
@@ -113,7 +115,7 @@
     private static final String NO_NAME = null;
     private static final String NO_SCREENSHOT = null;
     private static final String NO_TITLE = null;
-    private static final Integer NO_PID = null;
+    private static final int NO_PID = 0;
     private static final boolean RENAMED_SCREENSHOTS = true;
     private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
 
@@ -159,16 +161,20 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        assertProgressNotification(NAME, "0.00%");
+        final NumberFormat nf = NumberFormat.getPercentInstance();
+        nf.setMinimumFractionDigits(2);
+        nf.setMaximumFractionDigits(2);
+
+        assertProgressNotification(NAME, nf.format(0));
 
         SystemProperties.set(PROGRESS_PROPERTY, "108");
-        assertProgressNotification(NAME, "10.80%");
+        assertProgressNotification(NAME, nf.format(0.108));
 
         SystemProperties.set(PROGRESS_PROPERTY, "500");
-        assertProgressNotification(NAME, "50.00%");
+        assertProgressNotification(NAME, nf.format(0.50));
 
         SystemProperties.set(MAX_PROPERTY, "2000");
-        assertProgressNotification(NAME, "25.00%");
+        assertProgressNotification(NAME, nf.format(0.25));
 
         Bundle extras =
                 sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
@@ -198,11 +204,11 @@
         sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
 
         if (serviceDies) {
-            waitShareNotification();
+            waitShareNotification(PID);
             killService();
         }
 
-        Bundle extras = acceptBugreportAndGetSharedIntent();
+        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
                 NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
 
@@ -222,7 +228,7 @@
 
         sendBugreportStarted(1000);
         sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
-        waitShareNotification();
+        waitShareNotification(PID);
 
         // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
         Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
@@ -231,7 +237,7 @@
             killService();
         }
 
-        Bundle extras = acceptBugreportAndGetSharedIntent();
+        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
                 NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
 
@@ -243,7 +249,7 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
 
         // Check initial name.
         String actualName = detailsUi.nameField.getText().toString();
@@ -296,7 +302,7 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
 
         // Check initial name.
         String actualName = detailsUi.nameField.getText().toString();
@@ -326,7 +332,7 @@
         sendBugreportStarted(1000);
         waitForScreenshotButtonEnabled(true);
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
 
         detailsUi.nameField.setText("");
         detailsUi.titleField.setText("");
@@ -363,14 +369,14 @@
             waitForScreenshotButtonEnabled(true);
         }
 
-        DetailsUi detailsUi = new DetailsUi(mUiBot);
+        DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
 
         // Finish the bugreport while user's still typing the name.
         detailsUi.nameField.setText(NEW_NAME);
         sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
 
         // Wait until the share notification is received...
-        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+        waitShareNotification(PID);
         // ...then close notification bar.
         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
 
@@ -384,7 +390,7 @@
         detailsUi.clickOk();
 
         // Finally, share bugreport.
-        Bundle extras = acceptBugreportAndGetSharedIntent();
+        Bundle extras = acceptBugreportAndGetSharedIntent(PID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
                 NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
 
@@ -397,7 +403,7 @@
 
         // Send notification and click on share.
         sendBugreportFinished(NO_PID, mPlainTextPath, null);
-        acceptBugreport();
+        acceptBugreport(NO_PID);
 
         // Handle the warning
         mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
@@ -421,7 +427,7 @@
     public void testShareBugreportAfterServiceDies() throws Exception {
         sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
         killService();
-        Bundle extras = acceptBugreportAndGetSharedIntent();
+        Bundle extras = acceptBugreportAndGetSharedIntent(NO_PID);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
     }
 
@@ -457,14 +463,14 @@
     private void assertProgressNotification(String name, String percent) {
         // TODO: it currently looks for 3 distinct objects, without taking advantage of their
         // relationship.
-        openProgressNotification();
+        openProgressNotification(PID);
         Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
         mUiBot.getObject(name);
         mUiBot.getObject(percent);
     }
 
-    private void openProgressNotification() {
-        String title = mContext.getString(R.string.bugreport_in_progress_title);
+    private void openProgressNotification(int pid) {
+        String title = mContext.getString(R.string.bugreport_in_progress_title, pid);
         Log.v(TAG, "Looking for progress notification title: '" + title + "'");
         mUiBot.getNotification(title);
     }
@@ -494,7 +500,7 @@
      */
     private Bundle sendBugreportFinishedAndGetSharedIntent(String bugreportPath,
             String screenshotPath) {
-        return sendBugreportFinishedAndGetSharedIntent(null, bugreportPath, screenshotPath);
+        return sendBugreportFinishedAndGetSharedIntent(NO_PID, bugreportPath, screenshotPath);
     }
 
     /**
@@ -502,10 +508,10 @@
      *
      * @return extras sent in the shared intent.
      */
-    private Bundle sendBugreportFinishedAndGetSharedIntent(Integer pid, String bugreportPath,
+    private Bundle sendBugreportFinishedAndGetSharedIntent(int pid, String bugreportPath,
             String screenshotPath) {
         sendBugreportFinished(pid, bugreportPath, screenshotPath);
-        return acceptBugreportAndGetSharedIntent();
+        return acceptBugreportAndGetSharedIntent(pid);
     }
 
     /**
@@ -513,8 +519,8 @@
      *
      * @return extras sent in the shared intent.
      */
-    private Bundle acceptBugreportAndGetSharedIntent() {
-        acceptBugreport();
+    private Bundle acceptBugreportAndGetSharedIntent(int pid) {
+        acceptBugreport(pid);
         mUiBot.chooseActivity(UI_NAME);
         return mListener.getExtras();
     }
@@ -522,24 +528,24 @@
     /**
      * Waits for the notification to share the finished bugreport.
      */
-    private void waitShareNotification() {
-        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+    private void waitShareNotification(int pid) {
+        mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, pid));
     }
 
     /**
      * Accepts the notification to share the finished bugreport.
      */
-    private void acceptBugreport() {
-        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title));
+    private void acceptBugreport(int pid) {
+        mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, pid));
     }
 
     /**
      * Sends a "bugreport finished" intent.
      */
-    private void sendBugreportFinished(Integer pid, String bugreportPath, String screenshotPath) {
+    private void sendBugreportFinished(int pid, String bugreportPath, String screenshotPath) {
         Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        if (pid != null) {
+        if (pid != NO_PID) {
             intent.putExtra(EXTRA_PID, pid);
         }
         if (bugreportPath != null) {
@@ -775,7 +781,7 @@
      * Gets the notification button used to take a screenshot.
      */
     private UiObject getScreenshotButton() {
-        openProgressNotification();
+        openProgressNotification(PID);
         return mUiBot.getVisibleObject(
                 mContext.getString(R.string.bugreport_screenshot_action).toUpperCase());
     }
@@ -827,12 +833,15 @@
         /**
          * Gets the UI objects by opening the progress notification and clicking DETAILS.
          */
-        DetailsUi(UiBot uiBot) {
-            openProgressNotification();
+        DetailsUi(UiBot uiBot, int pid) throws UiObjectNotFoundException {
+            openProgressNotification(pid);
             detailsButton = mUiBot.getVisibleObject(
                     mContext.getString(R.string.bugreport_info_action).toUpperCase());
             mUiBot.click(detailsButton, "details_button");
             // TODO: unhardcode resource ids
+            UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle");
+            assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title,
+                    pid), dialogTitle.getText().toString());
             nameField = mUiBot.getVisibleObjectById("com.android.shell:id/name");
             titleField = mUiBot.getVisibleObjectById("com.android.shell:id/title");
             descField = mUiBot.getVisibleObjectById("com.android.shell:id/description");
@@ -849,7 +858,7 @@
         }
 
         void reOpen() {
-            openProgressNotification();
+            openProgressNotification(PID);
             mUiBot.click(detailsButton, "details_button");
 
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 3f482c8..3b49d37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -205,7 +205,7 @@
                 ? stack.indexOfStackTask(launchTarget)
                 : 0;
         boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
-        boolean animateNavBarScrim = true;
+        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
         mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
 
         // Keep track of whether we launched from the nav bar button or via alt-tab
@@ -460,13 +460,7 @@
         // wait on the system to send a signal that was never queued.
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        launchState.launchedFromHome = false;
-        launchState.launchedFromSearchHome = false;
-        launchState.launchedFromAppWithThumbnail = false;
-        launchState.launchedToTaskId = -1;
-        launchState.launchedWithAltTab = false;
-        launchState.launchedHasConfigurationChanged = false;
-        launchState.launchedViaDragGesture = false;
+        launchState.reset();
 
         MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 177e841..f7ebd94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -34,10 +34,22 @@
     public boolean launchedReuseTaskStackViews;
     public boolean launchedHasConfigurationChanged;
     public boolean launchedViaDragGesture;
+    public boolean launchedWhileDocking;
     public int launchedToTaskId;
     public int launchedNumVisibleTasks;
     public int launchedNumVisibleThumbnails;
 
+    public void reset() {
+        launchedFromHome = false;
+        launchedFromSearchHome = false;
+        launchedFromAppWithThumbnail = false;
+        launchedToTaskId = -1;
+        launchedWithAltTab = false;
+        launchedHasConfigurationChanged = false;
+        launchedViaDragGesture = false;
+        launchedWhileDocking = false;
+    }
+
     /** Called when the configuration has changed, and we want to reset any configuration specific
      * members. */
     public void updateOnConfigurationChange() {
@@ -46,6 +58,7 @@
         // Set this flag to indicate that the configuration has changed since Recents last launched
         launchedHasConfigurationChanged = true;
         launchedViaDragGesture = false;
+        launchedWhileDocking = false;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index dd7b7c1..5890b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -161,6 +161,7 @@
     boolean mCanReuseTaskStackViews = true;
     boolean mDraggingInRecents;
     boolean mReloadTasks;
+    boolean mLaunchedWhileDocking;
 
     // Task launching
     Rect mSearchBarBounds = new Rect();
@@ -270,10 +271,10 @@
     }
 
     public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
-            boolean animate, boolean reloadTasks) {
+            boolean animate, boolean launchedWhileDockingTask) {
         mTriggeredFromAltTab = triggeredFromAltTab;
         mDraggingInRecents = draggingInRecents;
-        mReloadTasks = reloadTasks;
+        mLaunchedWhileDocking = launchedWhileDockingTask;
         if (mFastAltTabTrigger.hasTriggered()) {
             // We are calling this from the doze trigger, so just fall through to show Recents
             mFastAltTabTrigger.resetTrigger();
@@ -338,6 +339,7 @@
         }
 
         mDraggingInRecents = false;
+        mLaunchedWhileDocking = false;
         mTriggeredFromAltTab = false;
 
         try {
@@ -865,11 +867,11 @@
         // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
         // should always preload the tasks now. If we are dragging in recents, reload them as
         // the stacks might have changed.
-        if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+        if (mLaunchedWhileDocking || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
             // Create a new load plan if preloadRecents() was never triggered
             sInstanceLoadPlan = loader.createLoadPlan(mContext);
         }
-        if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+        if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
             loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
         }
         TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -957,6 +959,7 @@
         launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
         launchState.launchedHasConfigurationChanged = false;
         launchState.launchedViaDragGesture = mDraggingInRecents;
+        launchState.launchedWhileDocking = mLaunchedWhileDocking;
 
         Intent intent = new Intent();
         intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index fb86214..42ebfa9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -287,13 +287,7 @@
         // wait on the system to send a signal that was never queued.
         RecentsConfiguration config = Recents.getConfiguration();
         RecentsActivityLaunchState launchState = config.getLaunchState();
-        launchState.launchedFromHome = false;
-        launchState.launchedFromSearchHome = false;
-        launchState.launchedFromAppWithThumbnail = false;
-        launchState.launchedToTaskId = -1;
-        launchState.launchedWithAltTab = false;
-        launchState.launchedHasConfigurationChanged = false;
-        launchState.launchedViaDragGesture = false;
+        launchState.reset();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5e113b9..d4624f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -185,7 +185,8 @@
         // If we are already occluded by the app, then just set the default background scrim now.
         // Otherwise, defer until the enter animation completes to animate the scrim with the
         // tasks for the home animation.
-        if (launchState.launchedFromAppWithThumbnail || mStack.getTaskCount() == 0) {
+        if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail
+                || mStack.getTaskCount() == 0) {
             mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
         } else {
             mBackgroundScrim.setAlpha(0);
@@ -645,7 +646,8 @@
 
     public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
-        if (!launchState.launchedFromAppWithThumbnail && mStack.getTaskCount() > 0) {
+        if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail
+                && mStack.getTaskCount() > 0) {
             animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
                     TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 12e2713..36cfac8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -73,6 +73,7 @@
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
+    private boolean mTouching;
 
     public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -86,6 +87,9 @@
     }
 
     public void setTouching(boolean touching, boolean animate) {
+        if (touching == mTouching) {
+            return;
+        }
         if (mAnimator != null) {
             mAnimator.cancel();
             mAnimator = null;
@@ -103,6 +107,7 @@
             animateToTarget(touching ? mCircleDiameter : mWidth,
                     touching ? mCircleDiameter : mHeight, touching);
         }
+        mTouching = touching;
     }
 
     private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 83c22b1..1bdf5a1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -100,6 +100,7 @@
     private final int[] mTempInt2 = new int[2];
     private boolean mMoving;
     private int mTouchSlop;
+    private boolean mBackgroundLifted;
 
     private int mDividerInsets;
     private int mDisplayWidth;
@@ -210,8 +211,8 @@
         mDockSide = mWindowManagerProxy.getDockSide();
         initializeSnapAlgorithm();
         mWindowManagerProxy.setResizing(true);
-        mWindowManager.setSlippery(false);
         if (touching) {
+            mWindowManager.setSlippery(false);
             liftBackground();
         }
         return mDockSide != WindowManager.DOCKED_INVALID;
@@ -389,6 +390,9 @@
     }
 
     private void liftBackground() {
+        if (mBackgroundLifted) {
+            return;
+        }
         if (isHorizontalDivision()) {
             mBackground.animate().scaleY(1.4f);
         } else {
@@ -407,9 +411,13 @@
                 .setDuration(TOUCH_ANIMATION_DURATION)
                 .translationZ(mTouchElevation)
                 .start();
+        mBackgroundLifted = true;
     }
 
     private void releaseBackground() {
+        if (!mBackgroundLifted) {
+            return;
+        }
         mBackground.animate()
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
@@ -422,6 +430,7 @@
                 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
                 .translationZ(0)
                 .start();
+        mBackgroundLifted = false;
     }
 
     @Override
@@ -485,7 +494,9 @@
         }
 
         // Make sure shadows are updated
-        mBackground.invalidate();
+        if (mBackground.getZ() > 0f) {
+            mBackground.invalidate();
+        }
 
         mLastResizeRect.set(mDockedRect);
         if (taskPosition != TASK_POSITION_SAME) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index ecba0a4..4dbb490 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -62,6 +62,10 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.LockSettingsStorage.CredentialHash;
 
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
 import java.util.Arrays;
 import java.util.List;
 
@@ -510,9 +514,9 @@
         }
     }
 
-    private void unlockUser(int userId, byte[] token) {
+    private void unlockUser(int userId, byte[] token, byte[] secret) {
         try {
-            ActivityManagerNative.getDefault().unlockUser(userId, token);
+            ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -560,6 +564,7 @@
             getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePatternHash(null, userId);
             setKeystorePassword(null, userId);
+            clearUserKeyProtection(userId);
             return;
         }
 
@@ -573,6 +578,7 @@
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
         if (enrolledHandle != null) {
             mStorage.writePatternHash(enrolledHandle, userId);
+            setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
         } else {
             throw new RemoteException("Failed to enroll pattern");
         }
@@ -588,6 +594,7 @@
             getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePasswordHash(null, userId);
             setKeystorePassword(null, userId);
+            clearUserKeyProtection(userId);
             return;
         }
 
@@ -601,6 +608,7 @@
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
         if (enrolledHandle != null) {
             mStorage.writePasswordHash(enrolledHandle, userId);
+            setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
         } else {
             throw new RemoteException("Failed to enroll password");
         }
@@ -633,6 +641,48 @@
         return hash;
     }
 
+    private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
+            throws RemoteException {
+        if (vcr == null) {
+            throw new RemoteException("Null response verifying a credential we just set");
+        }
+        if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+            throw new RemoteException("Non-OK response verifying a credential we just set: "
+                + vcr.getResponseCode());
+        }
+        byte[] token = vcr.getPayload();
+        if (token == null) {
+            throw new RemoteException("Empty payload verifying a credential we just set");
+        }
+        changeUserKey(userId, token, secretFromCredential(credential));
+    }
+
+    private void clearUserKeyProtection(int userId) throws RemoteException {
+        changeUserKey(userId, null, null);
+    }
+
+    private static byte[] secretFromCredential(String credential) throws RemoteException {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-512");
+            // Personalize the hash
+            byte[] personalization = "Android FBE credential hash"
+                    .getBytes(StandardCharsets.UTF_8);
+            // Pad it to the block size of the hash function
+            personalization = Arrays.copyOf(personalization, 128);
+            digest.update(personalization);
+            digest.update(credential.getBytes(StandardCharsets.UTF_8));
+            return digest.digest();
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
+        }
+    }
+
+    private void changeUserKey(int userId, byte[] token, byte[] secret)
+            throws RemoteException {
+        final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
+        getMountService().changeUserKey(userId, userInfo.serialNumber, token, null, secret);
+    }
+
     @Override
     public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
         return doVerifyPattern(pattern, false, 0, userId);
@@ -742,11 +792,11 @@
             if (Arrays.equals(hash, storedHash.hash)) {
                 unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
 
-                // TODO: pass through a meaningful token from gatekeeper to
-                // unlock credential keys; for now pass through a stub value to
-                // indicate that we came from a user challenge.
-                final byte[] token = String.valueOf(userId).getBytes();
-                unlockUser(userId, token);
+                // Users with legacy credentials don't have credential-backed
+                // FBE keys, so just pass through a fake token/secret
+                Slog.i(TAG, "Unlocking user with fake token: " + userId);
+                final byte[] fakeToken = String.valueOf(userId).getBytes();
+                unlockUser(userId, fakeToken, fakeToken);
 
                 // migrate credential to GateKeeper
                 credentialUtil.setCredential(credential, null, userId);
@@ -786,11 +836,9 @@
             // credential has matched
             unlockKeystore(credential, userId);
 
-            // TODO: pass through a meaningful token from gatekeeper to
-            // unlock credential keys; for now pass through a stub value to
-            // indicate that we came from a user challenge.
-            final byte[] token = String.valueOf(userId).getBytes();
-            unlockUser(userId, token);
+            Slog.i(TAG, "Unlocking user " + userId +
+                " with token length " + response.getPayload().length);
+            unlockUser(userId, response.getPayload(), secretFromCredential(credential));
 
             UserInfo info = UserManager.get(mContext).getUserInfo(userId);
             if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 5120e1b..cbd477a 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2742,8 +2742,30 @@
         }
     }
 
+    private SensitiveArg encodeBytes(byte[] bytes) {
+        if (ArrayUtils.isEmpty(bytes)) {
+            return new SensitiveArg("!");
+        } else {
+            return new SensitiveArg(HexDump.toHexString(bytes));
+        }
+    }
+
     @Override
-    public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+    public void changeUserKey(int userId, int serialNumber,
+            byte[] token, byte[] oldSecret, byte[] newSecret) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            mCryptConnector.execute("cryptfs", "change_user_key", userId, serialNumber,
+                encodeBytes(token), encodeBytes(oldSecret), encodeBytes(newSecret));
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
         enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
         waitForReady();
 
@@ -2753,16 +2775,9 @@
             throw new IllegalStateException("Token required to unlock secure user " + userId);
         }
 
-        final String encodedToken;
-        if (ArrayUtils.isEmpty(token)) {
-            encodedToken = "!";
-        } else {
-            encodedToken = HexDump.toHexString(token);
-        }
-
         try {
             mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
-                    new SensitiveArg(encodedToken));
+                    encodeBytes(token), encodeBytes(secret));
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5125133..9dae740 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20340,8 +20340,8 @@
     }
 
     @Override
-    public boolean unlockUser(int userId, byte[] token) {
-        return mUserController.unlockUser(userId, token);
+    public boolean unlockUser(int userId, byte[] token, byte[] secret) {
+        return mUserController.unlockUser(userId, token, secret);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c352fc8..0bccffa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1097,6 +1097,9 @@
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             r.stopped = true;
             r.state = ActivityState.STOPPED;
+
+            mWindowManager.notifyAppStopped(r.appToken);
+
             if (getVisibleBehindActivity() == r) {
                 mStackSupervisor.requestVisibleBehindLocked(r, false);
             }
@@ -1561,7 +1564,7 @@
                             resumeNextActivity = false;
                         }
                     } else {
-                        makeVisible(starting, r);
+                        makeVisibleIfNeeded(starting, r);
                     }
                     // Aggregate current change flags.
                     configChanges |= r.configChangeFlags;
@@ -1719,28 +1722,30 @@
         return behindFullscreenActivity;
     }
 
-    private void makeVisible(ActivityRecord starting, ActivityRecord r) {
+    private void makeVisibleIfNeeded(ActivityRecord starting, ActivityRecord r) {
+
         // This activity is not currently visible, but is running. Tell it to become visible.
-        r.visible = true;
-        if (r.state != ActivityState.RESUMED && r != starting) {
-            // If this activity is paused, tell it to now show its window.
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                    "Making visible and scheduling visibility: " + r);
-            try {
-                if (mTranslucentActivityWaiting != null) {
-                    r.updateOptionsLocked(r.returningOptions);
-                    mUndrawnActivitiesBelowTopTranslucent.add(r);
-                }
-                setVisible(r, true);
-                r.sleeping = false;
-                r.app.pendingUiClean = true;
-                r.app.thread.scheduleWindowVisibility(r.appToken, true);
-                r.stopFreezingScreenLocked(false);
-            } catch (Exception e) {
-                // Just skip on any failure; we'll make it
-                // visible when it next restarts.
-                Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
+        if (r.state == ActivityState.RESUMED || r == starting) {
+            return;
+        }
+
+        // If this activity is paused, tell it to now show its window.
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                "Making visible and scheduling visibility: " + r);
+        try {
+            if (mTranslucentActivityWaiting != null) {
+                r.updateOptionsLocked(r.returningOptions);
+                mUndrawnActivitiesBelowTopTranslucent.add(r);
             }
+            setVisible(r, true);
+            r.sleeping = false;
+            r.app.pendingUiClean = true;
+            r.app.thread.scheduleWindowVisibility(r.appToken, true);
+            r.stopFreezingScreenLocked(false);
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it
+            // visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f53e71a..0beef53 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1932,7 +1932,7 @@
 
     private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r,
             boolean preserveWindows) {
-        if (r == null) {
+        if (r == null || !r.visible) {
             return;
         }
         final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2f63b2d3..a355fa4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -783,7 +783,7 @@
         return result;
     }
 
-    boolean unlockUser(final int userId, byte[] token) {
+    boolean unlockUser(final int userId, byte[] token, byte[] secret) {
         if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: unlockUser() from pid="
@@ -796,7 +796,7 @@
 
         final long binderToken = Binder.clearCallingIdentity();
         try {
-            return unlockUserCleared(userId, token);
+            return unlockUserCleared(userId, token, secret);
         } finally {
             Binder.restoreCallingIdentity(binderToken);
         }
@@ -810,10 +810,10 @@
      */
     boolean maybeUnlockUser(final int userId) {
         // Try unlocking storage using empty token
-        return unlockUserCleared(userId, null);
+        return unlockUserCleared(userId, null, null);
     }
 
-    boolean unlockUserCleared(final int userId, byte[] token) {
+    boolean unlockUserCleared(final int userId, byte[] token, byte[] secret) {
         synchronized (mService) {
             // Bail if already running unlocked
             final UserState uss = mStartedUsers.get(userId);
@@ -824,7 +824,7 @@
             final UserInfo userInfo = getUserInfo(userId);
             final IMountService mountService = getMountService();
             try {
-                mountService.unlockUserKey(userId, userInfo.serialNumber, token);
+                mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
             } catch (RemoteException | RuntimeException e) {
                 Slog.w(TAG, "Failed to unlock: " + e.getMessage());
                 return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ada7458..c8645b4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -70,6 +71,7 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
@@ -357,6 +359,7 @@
     static final int SCAN_REQUIRE_KNOWN = 1<<12;
     static final int SCAN_MOVE = 1<<13;
     static final int SCAN_INITIAL = 1<<14;
+    static final int SCAN_CHECK_ONLY = 1<<15;
 
     static final int REMOVE_CHATTY = 1<<16;
 
@@ -1425,6 +1428,7 @@
                         PackageInstalledInfo res = data.res;
 
                         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                            //TODO: Broadcast for child packages too
                             final String packageName = res.pkg.applicationInfo.packageName;
                             res.removedInfo.sendBroadcast(false, true, false);
                             Bundle extras = new Bundle(1);
@@ -1886,11 +1890,20 @@
         }
     }
 
+    void scheduleWritePackageRestrictionsLocked(UserHandle user) {
+        final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
+        scheduleWritePackageRestrictionsLocked(userId);
+    }
+
     void scheduleWritePackageRestrictionsLocked(int userId) {
-        if (!sUserManager.exists(userId)) return;
-        mDirtyUsers.add(userId);
-        if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
-            mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+        final int[] userIds = (userId == UserHandle.USER_ALL)
+                ? sUserManager.getUserIds() : new int[]{userId};
+        for (int nextUserId : userIds) {
+            if (!sUserManager.exists(nextUserId)) return;
+            mDirtyUsers.add(nextUserId);
+            if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
+                mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+            }
         }
     }
 
@@ -2216,7 +2229,7 @@
                                     + ps.codePathString + ", installStatus=" + ps.installStatus
                                     + ", versionCode=" + ps.versionCode + "; scanned versionCode="
                                     + scannedPkg.mVersionCode);
-                            removePackageLI(ps, true);
+                            removePackageSettingLI(scannedPkg, true);
                             mExpectingBetter.put(ps.name, ps.codePath);
                         }
 
@@ -6263,9 +6276,8 @@
         }
     }
 
-    private void collectCertificatesLI(PackageParser pp, PackageSetting ps,
-            PackageParser.Package pkg, File srcFile, int parseFlags)
-            throws PackageManagerException {
+    private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
+            int parseFlags) throws PackageManagerException {
         if (ps != null
                 && ps.codePath.equals(srcFile)
                 && ps.timeStamp == srcFile.lastModified()
@@ -6294,7 +6306,7 @@
         }
 
         try {
-            pp.collectCertificates(pkg, parseFlags);
+            PackageParser.collectCertificates(pkg, parseFlags);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
         }
@@ -6338,6 +6350,56 @@
             throw PackageManagerException.from(e);
         }
 
+        return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+    }
+
+    /**
+     *  Scans a package and returns the newly parsed package.
+     *  @throws PackageManagerException on a parse error.
+     */
+    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
+            int parseFlags, int scanFlags, long currentTime, UserHandle user)
+            throws PackageManagerException {
+        // If the package has children and this is the first dive in the function
+        // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
+        // packages (parent and children) would be successfully scanned before the
+        // actual scan since scanning mutates internal state and we want to atomically
+        // install the package and its children.
+        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
+                scanFlags |= SCAN_CHECK_ONLY;
+            }
+        } else {
+            scanFlags &= ~SCAN_CHECK_ONLY;
+        }
+
+        // Scan the parent
+        PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, parseFlags,
+                scanFlags, currentTime, user);
+
+        // Scan the children
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPackage = pkg.childPackages.get(i);
+            scanPackageInternalLI(childPackage, scanFile, parseFlags, scanFlags,
+                    currentTime, user);
+        }
+
+
+        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+            return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+        }
+
+        return scannedPkg;
+    }
+
+    /**
+     *  Scans a package and returns the newly parsed package.
+     *  @throws PackageManagerException on a parse error.
+     */
+    private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
+            int parseFlags, int scanFlags, long currentTime, UserHandle user)
+            throws PackageManagerException {
         PackageSetting ps = null;
         PackageSetting updatedPkg;
         // reader
@@ -6358,7 +6420,39 @@
             // package name depending on our state.
             updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
             if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
+
+            // If this is a package we don't know about on the system partition, we
+            // may need to remove disabled child packages on the system partition
+            // or may need to not add child packages if the parent apk is updated
+            // on the data partition and no longer defines this child package.
+            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
+                // If this is a parent package for an updated system app and this system
+                // app got an OTA update which no longer defines some of the child packages
+                // we have to prune them from the disabled system packages.
+                PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+                if (disabledPs != null) {
+                    final int scannedChildCount = (pkg.childPackages != null)
+                            ? pkg.childPackages.size() : 0;
+                    final int disabledChildCount = disabledPs.childPackageNames != null
+                            ? disabledPs.childPackageNames.size() : 0;
+                    for (int i = 0; i < disabledChildCount; i++) {
+                        String disabledChildPackageName = disabledPs.childPackageNames.get(i);
+                        boolean disabledPackageAvailable = false;
+                        for (int j = 0; j < scannedChildCount; j++) {
+                            PackageParser.Package childPkg = pkg.childPackages.get(j);
+                            if (childPkg.packageName.equals(disabledChildPackageName)) {
+                                disabledPackageAvailable = true;
+                                break;
+                            }
+                         }
+                         if (!disabledPackageAvailable) {
+                             mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
+                         }
+                    }
+                }
+            }
         }
+
         boolean updatedPkgBetter = false;
         // First check if this is a system package that may involve an update
         if (updatedPkg != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
@@ -6391,10 +6485,24 @@
                         updatedPkg.resourcePathString = scanFile.toString();
                     }
                     updatedPkg.pkg = pkg;
-                    throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                            "Package " + ps.name + " at " + scanFile
-                                    + " ignored: updated version " + ps.versionCode
-                                    + " better than this " + pkg.mVersionCode);
+                    updatedPkg.versionCode = pkg.mVersionCode;
+
+                    // Update the disabled system child packages to point to the package too.
+                    final int childCount = updatedPkg.childPackageNames != null
+                            ? updatedPkg.childPackageNames.size() : 0;
+                    for (int i = 0; i < childCount; i++) {
+                        String childPackageName = updatedPkg.childPackageNames.get(i);
+                        PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
+                                childPackageName);
+                        if (updatedChildPkg != null) {
+                            updatedChildPkg.pkg = pkg;
+                            updatedChildPkg.versionCode = pkg.mVersionCode;
+                        }
+                    }
+
+                    throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
+                            + scanFile + " ignored: updated version " + ps.versionCode
+                            + " better than this " + pkg.mVersionCode);
                 } else {
                     // The current app on the system partition is better than
                     // what we have updated to on the data partition; switch
@@ -6439,7 +6547,7 @@
         }
 
         // Verify certificates against what was last scanned
-        collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
+        collectCertificatesLI(ps, pkg, scanFile, parseFlags);
 
         /*
          * A new system app appeared, but we already had a non-system one of the
@@ -6456,7 +6564,7 @@
                     != PackageManager.SIGNATURE_MATCH) {
                 logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
                         + " signatures don't match existing userdata copy; removing");
-                deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+                deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false, null);
                 ps = null;
             } else {
                 /*
@@ -6515,13 +6623,13 @@
         }
 
         // Set application objects path explicitly.
-        pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
-        pkg.applicationInfo.setCodePath(pkg.codePath);
-        pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
-        pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
-        pkg.applicationInfo.setResourcePath(resourcePath);
-        pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
-        pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+        pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+        pkg.setApplicationInfoCodePath(pkg.codePath);
+        pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+        pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+        pkg.setApplicationInfoResourcePath(resourcePath);
+        pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
+        pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
@@ -6534,7 +6642,7 @@
          */
         if (shouldHideSystemApp) {
             synchronized (mPackages) {
-                mSettings.disableSystemPackageLPw(pkg.packageName);
+                mSettings.disableSystemPackageLPw(pkg.packageName, true);
             }
         }
 
@@ -6932,17 +7040,62 @@
     }
 
     private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+        if (pkg == null) {
+            Slog.w(TAG, "Failed to delete code cache directory. No package: " + packageName);
+            return;
+        }
+        deleteCodeCacheDirsLI(pkg);
+    }
+
+    private void deleteCodeCacheDirsLI(PackageParser.Package pkg) {
         // TODO: triage flags as part of 26466827
         final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
 
-        final int[] users = sUserManager.getUserIds();
+        int[] users = sUserManager.getUserIds();
+        int res = 0;
         for (int user : users) {
+            // Remove the parent code cache
             try {
-                mInstaller.clearAppData(volumeUuid, packageName, user,
+                mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, user,
                         flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
             } catch (InstallerException e) {
                 Slog.w(TAG, "Failed to delete code cache directory", e);
             }
+            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                PackageParser.Package childPkg = pkg.childPackages.get(i);
+                // Remove the child code cache
+                try {
+                    mInstaller.clearAppData(childPkg.volumeUuid, childPkg.packageName,
+                            user, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+                } catch (InstallerException e) {
+                    Slog.w(TAG, "Failed to delete code cache directory", e);
+                }
+            }
+        }
+    }
+
+    private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime,
+            long lastUpdateTime) {
+        // Set parent install/update time
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps != null) {
+            ps.firstInstallTime = firstInstallTime;
+            ps.lastUpdateTime = lastUpdateTime;
+        }
+        // Set children install/update time
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            ps = (PackageSetting) childPkg.mExtras;
+            if (ps != null) {
+                ps.firstInstallTime = firstInstallTime;
+                ps.lastUpdateTime = lastUpdateTime;
+            }
         }
     }
 
@@ -7066,11 +7219,39 @@
     private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg, int parseFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+        // If the package has children and this is the first dive in the function
+        // we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
+        // whether all packages (parent and children) would be successfully scanned
+        // before the actual scan since scanning mutates internal state and we want
+        // to atomically install the package and its children.
+        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
+                scanFlags |= SCAN_CHECK_ONLY;
+            }
+        } else {
+            scanFlags &= ~SCAN_CHECK_ONLY;
+        }
+
+        final PackageParser.Package scannedPkg;
         try {
-            return scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+            // Scan the parent
+            scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+            // Scan the children
+            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                PackageParser.Package childPkg = pkg.childPackages.get(i);
+                scanPackageLI(childPkg, parseFlags,
+                        scanFlags, currentTime, user);
+            }
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
+
+        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+            return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
+        }
+
+        return scannedPkg;
     }
 
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
@@ -7089,7 +7270,8 @@
     }
 
     private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
-            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+            int scanFlags, long currentTime, UserHandle user)
+            throws PackageManagerException {
         final File scanFile = new File(pkg.codePath);
         if (pkg.applicationInfo.getCodePath() == null ||
                 pkg.applicationInfo.getResourcePath() == null) {
@@ -7125,28 +7307,30 @@
                             "Core android package being redefined.  Skipping.");
                 }
 
-                // Set up information for our fall-back user intent resolution activity.
-                mPlatformPackage = pkg;
-                pkg.mVersionCode = mSdkVersion;
-                mAndroidApplication = pkg.applicationInfo;
+                if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+                    // Set up information for our fall-back user intent resolution activity.
+                    mPlatformPackage = pkg;
+                    pkg.mVersionCode = mSdkVersion;
+                    mAndroidApplication = pkg.applicationInfo;
 
-                if (!mResolverReplaced) {
-                    mResolveActivity.applicationInfo = mAndroidApplication;
-                    mResolveActivity.name = ResolverActivity.class.getName();
-                    mResolveActivity.packageName = mAndroidApplication.packageName;
-                    mResolveActivity.processName = "system:ui";
-                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
-                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-                    mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
-                    mResolveActivity.exported = true;
-                    mResolveActivity.enabled = true;
-                    mResolveInfo.activityInfo = mResolveActivity;
-                    mResolveInfo.priority = 0;
-                    mResolveInfo.preferredOrder = 0;
-                    mResolveInfo.match = 0;
-                    mResolveComponentName = new ComponentName(
-                            mAndroidApplication.packageName, mResolveActivity.name);
+                    if (!mResolverReplaced) {
+                        mResolveActivity.applicationInfo = mAndroidApplication;
+                        mResolveActivity.name = ResolverActivity.class.getName();
+                        mResolveActivity.packageName = mAndroidApplication.packageName;
+                        mResolveActivity.processName = "system:ui";
+                        mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                        mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
+                        mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+                        mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
+                        mResolveActivity.exported = true;
+                        mResolveActivity.enabled = true;
+                        mResolveInfo.activityInfo = mResolveActivity;
+                        mResolveInfo.priority = 0;
+                        mResolveInfo.preferredOrder = 0;
+                        mResolveInfo.match = 0;
+                        mResolveComponentName = new ComponentName(
+                                mAndroidApplication.packageName, mResolveActivity.name);
+                    }
                 }
             }
         }
@@ -7156,39 +7340,43 @@
                 Log.d(TAG, "Scanning package " + pkg.packageName);
         }
 
-        if (mPackages.containsKey(pkg.packageName)
-                || mSharedLibraries.containsKey(pkg.packageName)) {
-            throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                    "Application package " + pkg.packageName
-                    + " already installed.  Skipping duplicate.");
-        }
+        synchronized (mPackages) {
+            if (mPackages.containsKey(pkg.packageName)
+                    || mSharedLibraries.containsKey(pkg.packageName)) {
+                throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+                        "Application package " + pkg.packageName
+                                + " already installed.  Skipping duplicate.");
+            }
 
-        // If we're only installing presumed-existing packages, require that the
-        // scanned APK is both already known and at the path previously established
-        // for it.  Previously unknown packages we pick up normally, but if we have an
-        // a priori expectation about this package's install presence, enforce it.
-        // With a singular exception for new system packages. When an OTA contains
-        // a new system package, we allow the codepath to change from a system location
-        // to the user-installed location. If we don't allow this change, any newer,
-        // user-installed version of the application will be ignored.
-        if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
-            if (mExpectingBetter.containsKey(pkg.packageName)) {
-                logCriticalInfo(Log.WARN,
-                        "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
-            } else {
-                PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
-                if (known != null) {
-                    if (DEBUG_PACKAGE_SCANNING) {
-                        Log.d(TAG, "Examining " + pkg.codePath
-                                + " and requiring known paths " + known.codePathString
-                                + " & " + known.resourcePathString);
-                    }
-                    if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
-                            || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
-                        throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
-                                "Application package " + pkg.packageName
-                                + " found at " + pkg.applicationInfo.getCodePath()
-                                + " but expected at " + known.codePathString + "; ignoring.");
+            // If we're only installing presumed-existing packages, require that the
+            // scanned APK is both already known and at the path previously established
+            // for it.  Previously unknown packages we pick up normally, but if we have an
+            // a priori expectation about this package's install presence, enforce it.
+            // With a singular exception for new system packages. When an OTA contains
+            // a new system package, we allow the codepath to change from a system location
+            // to the user-installed location. If we don't allow this change, any newer,
+            // user-installed version of the application will be ignored.
+            if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+                if (mExpectingBetter.containsKey(pkg.packageName)) {
+                    logCriticalInfo(Log.WARN,
+                            "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
+                } else {
+                    PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
+                    if (known != null) {
+                        if (DEBUG_PACKAGE_SCANNING) {
+                            Log.d(TAG, "Examining " + pkg.codePath
+                                    + " and requiring known paths " + known.codePathString
+                                    + " & " + known.resourcePathString);
+                        }
+                        if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
+                                || !pkg.applicationInfo.getResourcePath().equals(
+                                known.resourcePathString)) {
+                            throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+                                    "Application package " + pkg.packageName
+                                            + " found at " + pkg.applicationInfo.getCodePath()
+                                            + " but expected at " + known.codePathString
+                                            + "; ignoring.");
+                        }
                     }
                 }
             }
@@ -7208,6 +7396,11 @@
             pkg.mAdoptPermissions = null;
         }
 
+        // Getting the package setting may have a side-effect, so if we
+        // are only checking if scan would succeed, stash a copy of the
+        // old setting to restore at the end.
+        PackageSetting nonMutatedPs = null;
+
         // writer
         synchronized (mPackages) {
             if (pkg.mSharedUserId != null) {
@@ -7279,6 +7472,14 @@
                         + " was transferred to another, but its .apk remains");
             }
 
+            // See comments in nonMutatedPs declaration
+            if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+                PackageSetting foundPs = mSettings.peekPackageLPr(pkg.packageName);
+                if (foundPs != null) {
+                    nonMutatedPs = new PackageSetting(foundPs);
+                }
+            }
+
             // Just create the setting, don't add it yet. For already existing packages
             // the PkgSetting exists already and doesn't have to be created.
             pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
@@ -7305,13 +7506,15 @@
                 reportSettingsProblem(Log.WARN, msg);
 
                 // Make a note of it.
-                mTransferedPackages.add(origPackage.name);
+                if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+                    mTransferedPackages.add(origPackage.name);
+                }
 
                 // No longer need to retain this.
                 pkgSetting.origPackage = null;
             }
 
-            if (realName != null) {
+            if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
                 // Make a note of it.
                 mTransferedPackages.add(pkg.packageName);
             }
@@ -7413,7 +7616,7 @@
                 }
             }
 
-            if (pkg.mAdoptPermissions != null) {
+            if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
                 // This package wants to adopt ownership of permissions from
                 // another package.
                 for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
@@ -7537,6 +7740,33 @@
 
         ArrayList<PackageParser.Package> clientLibPkgs = null;
 
+        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+            if (nonMutatedPs != null) {
+                synchronized (mPackages) {
+                    mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
+                }
+            }
+            return pkg;
+        }
+
+        // Only privileged apps and updated privileged apps can add child packages.
+        if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
+            if ((parseFlags & PARSE_IS_PRIVILEGED) == 0) {
+                throw new PackageManagerException("Only privileged apps and updated "
+                        + "privileged apps can add child packages. Ignoring package "
+                        + pkg.packageName);
+            }
+            final int childCount = pkg.childPackages.size();
+            for (int i = 0; i < childCount; i++) {
+                PackageParser.Package childPkg = pkg.childPackages.get(i);
+                if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,
+                        childPkg.packageName)) {
+                    throw new PackageManagerException("Cannot override a child package of "
+                            + "another disabled system app. Ignoring package " + pkg.packageName);
+                }
+            }
+        }
+
         // writer
         synchronized (mPackages) {
             if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -7821,7 +8051,7 @@
 
                 // Now that permission groups have a special meaning, we ignore permission
                 // groups for legacy apps to prevent unexpected behavior. In particular,
-                // permissions for one app being granted to someone just becuase they happen
+                // permissions for one app being granted to someone just becase they happen
                 // to be in a group defined by another app (before this had no implications).
                 if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
                     p.group = mPermissionGroups.get(p.info.group);
@@ -8466,6 +8696,17 @@
         }
     }
 
+    private void killPackage(PackageParser.Package pkg, String reason) {
+        // Kill the parent package
+        killApplication(pkg.packageName, pkg.applicationInfo.uid, reason);
+        // Kill the child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            killApplication(childPkg.packageName, childPkg.applicationInfo.uid, reason);
+        }
+    }
+
     private void killApplication(String pkgName, int appId, String reason) {
         // Request the ActivityManager to kill the process(only for existing packages)
         // so that we do not end up in a confused state while the user is still using the older
@@ -8479,7 +8720,24 @@
         }
     }
 
-    void removePackageLI(PackageSetting ps, boolean chatty) {
+    private void removePackageSettingLI(PackageParser.Package pkg, boolean chatty) {
+        // Remove the parent package setting
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps != null) {
+            removePackageSettingLI(ps, chatty);
+        }
+        // Remove the child package setting
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            ps = (PackageSetting) childPkg.mExtras;
+            if (ps != null) {
+                removePackageSettingLI(ps, chatty);
+            }
+        }
+    }
+
+    void removePackageSettingLI(PackageSetting ps, boolean chatty) {
         if (DEBUG_INSTALL) {
             if (chatty)
                 Log.d(TAG, "Removing package " + ps.name);
@@ -8503,8 +8761,17 @@
 
         // writer
         synchronized (mPackages) {
+            // Remove the parent package
             mPackages.remove(pkg.applicationInfo.packageName);
             cleanPackageDataStructuresLILPw(pkg, chatty);
+
+            // Remove the child packages
+            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                PackageParser.Package childPkg = pkg.childPackages.get(i);
+                mPackages.remove(childPkg.applicationInfo.packageName);
+                cleanPackageDataStructuresLILPw(childPkg, chatty);
+            }
         }
     }
 
@@ -8708,6 +8975,17 @@
     static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
     static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
 
+    private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
+        // Update the parent permissions
+        updatePermissionsLPw(pkg.packageName, pkg, flags);
+        // Update the child permissions
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            updatePermissionsLPw(childPkg.packageName, childPkg, flags);
+        }
+    }
+
     private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
             int flags) {
         final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
@@ -9125,7 +9403,7 @@
                 if (pkg.isUpdatedSystemApp()) {
                     final PackageSetting sysPs = mSettings
                             .getDisabledSystemPkgLPr(pkg.packageName);
-                    if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
+                    if (sysPs != null && sysPs.getPermissionsState().hasInstallPermission(perm)) {
                         // If the original was granted this permission, we take
                         // that grant decision as read and propagate it to the
                         // update.
@@ -9139,16 +9417,38 @@
                         // before.  In this case we do want to allow the app to
                         // now get the new permission if the ancestral apk is
                         // privileged to get it.
-                        if (sysPs.pkg != null && sysPs.isPrivileged()) {
-                            for (int j=0;
-                                    j<sysPs.pkg.requestedPermissions.size(); j++) {
-                                if (perm.equals(
-                                        sysPs.pkg.requestedPermissions.get(j))) {
+                        if (sysPs != null && sysPs.pkg != null && sysPs.isPrivileged()) {
+                            for (int j = 0; j < sysPs.pkg.requestedPermissions.size(); j++) {
+                                if (perm.equals(sysPs.pkg.requestedPermissions.get(j))) {
                                     allowed = true;
                                     break;
                                 }
                             }
                         }
+                        // Also if a privileged parent package on the system image or any of
+                        // its children requested a privileged permission, the updated child
+                        // packages can also get the permission.
+                        if (pkg.parentPackage != null) {
+                            final PackageSetting disabledSysParentPs = mSettings
+                                    .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
+                            if (disabledSysParentPs != null && disabledSysParentPs.pkg != null
+                                    && disabledSysParentPs.isPrivileged()) {
+                                if (isPackageRequestingPermission(disabledSysParentPs.pkg, perm)) {
+                                    allowed = true;
+                                } else if (disabledSysParentPs.pkg.childPackages != null) {
+                                    final int count = disabledSysParentPs.pkg.childPackages.size();
+                                    for (int i = 0; i < count; i++) {
+                                        PackageParser.Package disabledSysChildPkg =
+                                                disabledSysParentPs.pkg.childPackages.get(i);
+                                        if (isPackageRequestingPermission(disabledSysChildPkg,
+                                                perm)) {
+                                            allowed = true;
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
                     }
                 } else {
                     allowed = isPrivilegedApp(pkg);
@@ -9192,6 +9492,17 @@
         return allowed;
     }
 
+    private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
+        final int permCount = pkg.requestedPermissions.size();
+        for (int j = 0; j < permCount; j++) {
+            String requestedPermission = pkg.requestedPermissions.get(j);
+            if (permission.equals(requestedPermission)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     final class ActivityIntentResolver
             extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
         public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
@@ -11780,20 +12091,20 @@
             resourceFile = afterCodeFile;
 
             // Reflect the rename in scanned details
-            pkg.codePath = afterCodeFile.getAbsolutePath();
-            pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                    pkg.baseCodePath);
-            pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                    pkg.splitCodePaths);
+            pkg.setCodePath(afterCodeFile.getAbsolutePath());
+            pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, pkg.baseCodePath));
+            pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, pkg.splitCodePaths));
 
             // Reflect the rename in app info
-            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
-            pkg.applicationInfo.setCodePath(pkg.codePath);
-            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
-            pkg.applicationInfo.setResourcePath(pkg.codePath);
-            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+            pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+            pkg.setApplicationInfoCodePath(pkg.codePath);
+            pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+            pkg.setApplicationInfoResourcePath(pkg.codePath);
+            pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
             return true;
         }
@@ -12035,20 +12346,20 @@
             final File afterCodeFile = new File(packagePath);
 
             // Reflect the rename in scanned details
-            pkg.codePath = afterCodeFile.getAbsolutePath();
-            pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                    pkg.baseCodePath);
-            pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
-                    pkg.splitCodePaths);
+            pkg.setCodePath(afterCodeFile.getAbsolutePath());
+            pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, pkg.baseCodePath));
+            pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, pkg.splitCodePaths));
 
             // Reflect the rename in app info
-            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
-            pkg.applicationInfo.setCodePath(pkg.codePath);
-            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
-            pkg.applicationInfo.setResourcePath(pkg.codePath);
-            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+            pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+            pkg.setApplicationInfoCodePath(pkg.codePath);
+            pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+            pkg.setApplicationInfoResourcePath(pkg.codePath);
+            pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
             return true;
         }
@@ -12227,13 +12538,13 @@
             }
 
             // Reflect the move in app info
-            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
-            pkg.applicationInfo.setCodePath(pkg.codePath);
-            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
-            pkg.applicationInfo.setResourcePath(pkg.codePath);
-            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
-            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+            pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+            pkg.setApplicationInfoCodePath(pkg.codePath);
+            pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+            pkg.setApplicationInfoResourcePath(pkg.codePath);
+            pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+            pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
             return true;
         }
@@ -12427,7 +12738,7 @@
             PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
                     System.currentTimeMillis(), user);
 
-            updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
+            updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
             prepareAppDataAfterInstall(newPackage);
 
             // delete the partially installed application. the data directory will have to be
@@ -12439,7 +12750,7 @@
                 // install.
                 deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
                         dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
-                                res.removedInfo, true);
+                                res.removedInfo, true, null);
             }
 
         } catch (PackageManagerException e) {
@@ -12508,16 +12819,16 @@
             if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
-                if(!checkUpgradeKeySetLP(ps, pkg)) {
+                if (!checkUpgradeKeySetLP(ps, pkg)) {
                     res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "New package not signed by keys specified by upgrade-keysets: "
-                            + pkgName);
+                                    + pkgName);
                     return;
                 }
             } else {
                 // default to original signature matching
                 if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
-                    != PackageManager.SIGNATURE_MATCH) {
+                        != PackageManager.SIGNATURE_MATCH) {
                     res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "New package has a different signature: " + pkgName);
                     return;
@@ -12535,33 +12846,30 @@
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
             replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+                    user, allUsers, perUserInstalled, installerPackageName, res);
         } else {
             replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
-                    user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+                    user, allUsers, perUserInstalled, installerPackageName, res);
         }
     }
 
     private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
             int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
-            String volumeUuid, PackageInstalledInfo res) {
-        String pkgName = deletedPackage.packageName;
-        boolean deletedPkg = true;
-        boolean updatedSettings = false;
-
+            PackageInstalledInfo res) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
                 + deletedPackage);
-        long origUpdateTime;
-        if (pkg.mExtras != null) {
-            origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
-        } else {
-            origUpdateTime = 0;
-        }
+
+        String pkgName = deletedPackage.packageName;
+        boolean deletedPkg = true;
+        boolean addedPkg = false;
+
+        final long origUpdateTime = (pkg.mExtras != null)
+                ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
 
         // First delete the existing package while retaining the data directory
         if (!deletePackageLI(pkgName, null, true, null, null, PackageManager.DELETE_KEEP_DATA,
-                res.removedInfo, true)) {
+                res.removedInfo, true, pkg)) {
             // If the existing package wasn't successfully deleted
             res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
             deletedPkg = false;
@@ -12580,33 +12888,30 @@
                 sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
             }
 
-            deleteCodeCacheDirsLI(pkg.volumeUuid, pkgName);
+            deleteCodeCacheDirsLI(pkg);
+
             try {
                 final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
                         scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
-                updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+                updateSettingsLI(newPackage, installerPackageName, allUsers,
                         perUserInstalled, res, user);
                 prepareAppDataAfterInstall(newPackage);
-                updatedSettings = true;
+                addedPkg = true;
             } catch (PackageManagerException e) {
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
             }
         }
 
         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
-            // remove package from internal structures.  Note that we want deletePackageX to
-            // delete the package data and cache directories that it created in
-            // scanPackageLocked, unless those directories existed before we even tried to
-            // install.
-            if(updatedSettings) {
-                if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
-                deletePackageLI(
-                        pkgName, null, true, allUsers, perUserInstalled,
-                        PackageManager.DELETE_KEEP_DATA,
-                                res.removedInfo, true);
+            if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
+
+            // Revert all internal state mutations and added folders for the failed install
+            if (addedPkg) {
+                deletePackageLI(pkgName, null, true, allUsers, perUserInstalled,
+                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
             }
-            // Since we failed to install the new package we need to restore the old
-            // package that we deleted.
+
+            // Restore the old package
             if (deletedPkg) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
                 File restoreFile = new File(deletedPackage.codePath);
@@ -12624,14 +12929,17 @@
                             + e.getMessage());
                     return;
                 }
-                // Restore of old package succeeded. Update permissions.
-                // writer
+
                 synchronized (mPackages) {
-                    updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
-                            UPDATE_PERMISSIONS_ALL);
-                    // can downgrade to reader
+                    // Ensure the installer package name up to date
+                    setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+                    // Update permissions for restored package
+                    updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
                     mSettings.writeLPr();
                 }
+
                 Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
             }
         }
@@ -12640,89 +12948,100 @@
     private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
             PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
             int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
-            String volumeUuid, PackageInstalledInfo res) {
+            PackageInstalledInfo res) {
         if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                 + ", old=" + deletedPackage);
-        boolean disabledSystem = false;
-        boolean updatedSettings = false;
+
+        final boolean disabledSystem;
+
+        // Set the system/privileged flags as needed
         parseFlags |= PackageParser.PARSE_IS_SYSTEM;
-        if ((deletedPackage.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+        if ((deletedPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
                 != 0) {
             parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
         }
-        String packageName = deletedPackage.packageName;
-        if (packageName == null) {
-            res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                    "Attempt to delete null packageName.");
-            return;
-        }
-        PackageParser.Package oldPkg;
-        PackageSetting oldPkgSetting;
-        // reader
-        synchronized (mPackages) {
-            oldPkg = mPackages.get(packageName);
-            oldPkgSetting = mSettings.mPackages.get(packageName);
-            if((oldPkg == null) || (oldPkg.applicationInfo == null) ||
-                    (oldPkgSetting == null)) {
-                res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                        "Couldn't find package " + packageName + " information");
-                return;
-            }
-        }
 
-        killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");
+        // Kill package processes including services, providers, etc.
+        killPackage(deletedPackage, "replace sys pkg");
 
-        res.removedInfo.uid = oldPkg.applicationInfo.uid;
-        res.removedInfo.removedPackage = packageName;
+        // Report the result for the parent package only
+        res.removedInfo.uid = deletedPackage.applicationInfo.uid;
+        res.removedInfo.removedPackage = deletedPackage.packageName;
+
         // Remove existing system package
-        removePackageLI(oldPkgSetting, true);
-        // writer
-        synchronized (mPackages) {
-            disabledSystem = mSettings.disableSystemPackageLPw(packageName);
-            if (!disabledSystem && deletedPackage != null) {
-                // We didn't need to disable the .apk as a current system package,
-                // which means we are replacing another update that is already
-                // installed.  We need to make sure to delete the older one's .apk.
-                res.removedInfo.args = createInstallArgsForExisting(0,
-                        deletedPackage.applicationInfo.getCodePath(),
-                        deletedPackage.applicationInfo.getResourcePath(),
-                        getAppDexInstructionSets(deletedPackage.applicationInfo));
-            } else {
-                res.removedInfo.args = null;
-            }
+        removePackageSettingLI(deletedPackage, true);
+
+        disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
+        if (!disabledSystem) {
+            // We didn't need to disable the .apk as a current system package,
+            // which means we are replacing another update that is already
+            // installed.  We need to make sure to delete the older one's .apk.
+            res.removedInfo.args = createInstallArgsForExisting(0,
+                    deletedPackage.applicationInfo.getCodePath(),
+                    deletedPackage.applicationInfo.getResourcePath(),
+                    getAppDexInstructionSets(deletedPackage.applicationInfo));
+        } else {
+            res.removedInfo.args = null;
         }
 
         // Successfully disabled the old package. Now proceed with re-installation
-        deleteCodeCacheDirsLI(pkg.volumeUuid, packageName);
+        deleteCodeCacheDirsLI(pkg);
 
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
-        pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
 
         PackageParser.Package newPackage = null;
         try {
+            // Add the package to the internal data structures
             newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
-            if (newPackage.mExtras != null) {
-                final PackageSetting newPkgSetting = (PackageSetting) newPackage.mExtras;
-                newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime;
-                newPkgSetting.lastUpdateTime = System.currentTimeMillis();
 
-                // is the update attempting to change shared user? that isn't going to work...
-                if (oldPkgSetting.sharedUser != newPkgSetting.sharedUser) {
-                    res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
-                            "Forbidding shared user change from " + oldPkgSetting.sharedUser
-                            + " to " + newPkgSetting.sharedUser);
-                    updatedSettings = true;
-                }
+            // Set the update and install times
+            PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
+            setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
+                    System.currentTimeMillis());
+
+            // Check for shared user id changes
+            String invalidPackageName = getParentOrChildPackageChangedSharedUser(deletedPackage, newPackage);
+            if (invalidPackageName != null) {
+                res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+                        "Forbidding shared user change from " + deletedPkgSetting.sharedUser
+                                + " to " + invalidPackageName);
             }
 
+            // Update the package dynamic state if succeeded
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+                // Now that the install succeeded make sure we remove data
+                // directories for any child package the update removed.
+                final int deletedChildCount = (deletedPackage.childPackages != null)
+                        ? deletedPackage.childPackages.size() : 0;
+                final int newChildCount = (newPackage.childPackages != null)
+                        ? newPackage.childPackages.size() : 0;
+                for (int i = 0; i < deletedChildCount; i++) {
+                    PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
+                    boolean childPackageDeleted = true;
+                    for (int j = 0; j < newChildCount; j++) {
+                        PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
+                        if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+                            childPackageDeleted = false;
+                            break;
+                        }
+                    }
+                    if (childPackageDeleted) {
+                        PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
+                                deletedChildPkg.packageName);
+                        if (ps != null) {
+                            removePackageDataLI(ps, allUsers, perUserInstalled, null, 0, false);
+                        }
+                    }
+                }
+
+                updateSettingsLI(newPackage, installerPackageName, allUsers,
                         perUserInstalled, res, user);
                 prepareAppDataAfterInstall(newPackage);
-                updatedSettings = true;
             }
-
         } catch (PackageManagerException e) {
+            res.returnCode = INSTALL_FAILED_INTERNAL_ERROR;
             res.setError("Package couldn't be installed in " + pkg.codePath, e);
         }
 
@@ -12734,21 +13053,111 @@
             }
             // Add back the old system package
             try {
-                scanPackageTracedLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+                scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
             }
-            // Restore the old system information in Settings
+
             synchronized (mPackages) {
                 if (disabledSystem) {
-                    mSettings.enableSystemPackageLPw(packageName);
+                    enableSystemPackageLPw(deletedPackage);
                 }
-                if (updatedSettings) {
-                    mSettings.setInstallerPackageName(packageName,
-                            oldPkgSetting.installerPackageName);
-                }
+
+                // Ensure the installer package name up to date
+                setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+                // Update permissions for restored package
+                updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
                 mSettings.writeLPr();
             }
+
+            Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
+                    + " after failed upgrade");
+        }
+    }
+
+    /**
+     * Checks whether the parent or any of the child packages have a change shared
+     * user. For a package to be a valid update the shred users of the parent and
+     * the children should match. We may later support changing child shared users.
+     * @param oldPkg The updated package.
+     * @param newPkg The update package.
+     * @return The shared user that change between the versions.
+     */
+    private String getParentOrChildPackageChangedSharedUser(PackageParser.Package oldPkg,
+            PackageParser.Package newPkg) {
+        // Check parent shared user
+        if (!Objects.equals(oldPkg.mSharedUserId, newPkg.mSharedUserId)) {
+            return newPkg.packageName;
+        }
+        // Check child shared users
+        final int oldChildCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
+        final int newChildCount = (newPkg.childPackages != null) ? newPkg.childPackages.size() : 0;
+        for (int i = 0; i < newChildCount; i++) {
+            PackageParser.Package newChildPkg = newPkg.childPackages.get(i);
+            // If this child was present, did it have the same shared user?
+            for (int j = 0; j < oldChildCount; j++) {
+                PackageParser.Package oldChildPkg = oldPkg.childPackages.get(j);
+                if (newChildPkg.packageName.equals(oldChildPkg.packageName)
+                        && !Objects.equals(newChildPkg.mSharedUserId, oldChildPkg.mSharedUserId)) {
+                    return newChildPkg.packageName;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void removeNativeBinariesLI(PackageParser.Package pkg) {
+        // Remove the lib path for the parent package
+        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        if (ps != null) {
+            NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+        }
+        // Remove the lib path for the child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            ps = (PackageSetting) pkg.childPackages.get(i).mExtras;
+            if (ps != null) {
+                NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+            }
+        }
+    }
+
+    private void enableSystemPackageLPw(PackageParser.Package pkg) {
+        // Enable the parent package
+        mSettings.enableSystemPackageLPw(pkg.packageName);
+        // Enable the child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            mSettings.enableSystemPackageLPw(childPkg.packageName);
+        }
+    }
+
+    private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
+            PackageParser.Package newPkg) {
+        // Disable the parent package (parent always replaced)
+        boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);
+        // Disable the child packages
+        final int childCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = oldPkg.childPackages.get(i);
+            final boolean replace = newPkg.hasChildPackage(childPkg.packageName);
+            disabled |= mSettings.disableSystemPackageLPw(childPkg.packageName, replace);
+        }
+        return disabled;
+    }
+
+    private void setInstallerPackageNameLPw(PackageParser.Package pkg,
+            String installerPackageName) {
+        // Enable the parent package
+        mSettings.setInstallerPackageName(pkg.packageName, installerPackageName);
+        // Enable the child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPkg = pkg.childPackages.get(i);
+            mSettings.setInstallerPackageName(childPkg.packageName, installerPackageName);
         }
     }
 
@@ -12813,8 +13222,23 @@
     }
 
     private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
-            String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
-            UserHandle user) {
+            int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) {
+        // Update the parent package setting
+        updateSettingsInternalLI(newPackage, installerPackageName, allUsers, perUserInstalled,
+                res, user);
+        // Update the child packages setting
+        final int childCount = (newPackage.childPackages != null)
+                ? newPackage.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPackage = newPackage.childPackages.get(i);
+            updateSettingsInternalLI(childPackage, installerPackageName, allUsers, perUserInstalled,
+                    res, user);
+        }
+    }
+
+    private void updateSettingsInternalLI(PackageParser.Package newPackage,
+            String installerPackageName, int[] allUsers, boolean[] perUserInstalled,
+            PackageInstalledInfo res, UserHandle user) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
 
         String pkgName = newPackage.packageName;
@@ -12959,7 +13383,7 @@
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
-            pp.collectCertificates(pkg, parseFlags);
+            PackageParser.collectCertificates(pkg, parseFlags);
         } catch (PackageParserException e) {
             res.setError("Failed collect during installPackageLI", e);
             return;
@@ -12994,8 +13418,17 @@
                     if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                 }
 
-                // Prevent apps opting out from runtime permissions
+                // Child packages are installed through the parent package
+                if (pkg.parentPackage != null) {
+                    res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                            "Package " + pkg.packageName + " is child of package "
+                                    + pkg.parentPackage.parentPackage + ". Child packages "
+                                    + "can be updated only through the parent package.");
+                    return;
+                }
+
                 if (replace) {
+                    // Prevent apps opting out from runtime permissions
                     PackageParser.Package oldPackage = mPackages.get(pkgName);
                     final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
                     final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
@@ -13007,6 +13440,15 @@
                                         + " target SDK " + oldTargetSdk + " does.");
                         return;
                     }
+
+                    // Prevent installing of child packages
+                    if (oldPackage.parentPackage != null) {
+                        res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                                "Package " + pkg.packageName + " is child of package "
+                                        + oldPackage.parentPackage + ". Child packages "
+                                        + "can be updated only through the parent package.");
+                        return;
+                    }
                 }
             }
 
@@ -13080,7 +13522,6 @@
                     }
                 }
             }
-
         }
 
         if (systemApp) {
@@ -13526,9 +13967,8 @@
 
         synchronized (mInstallLock) {
             if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
-            res = deletePackageLI(packageName, removeForUser,
-                    true, allUsers, perUserInstalled,
-                    flags | REMOVE_CHATTY, info, true);
+            res = deletePackageLI(packageName, removeForUser, true, allUsers, perUserInstalled,
+                    flags | REMOVE_CHATTY, info, true, null);
             systemUpdate = info.isRemovedPackageSystemUpdate;
             synchronized (mPackages) {
                 if (res) {
@@ -13617,7 +14057,7 @@
             PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = ps.name;
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
-        removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
+        removePackageSettingLI(ps, (flags&REMOVE_CHATTY) != 0);
         // Retrieve object to delete permissions for shared user later on
         final PackageSetting deletedPs;
         // reader
@@ -13713,27 +14153,36 @@
     /*
      * Tries to delete system package.
      */
-    private boolean deleteSystemPackageLI(PackageSetting newPs,
-            int[] allUserHandles, boolean[] perUserInstalled,
-            int flags, PackageRemovedInfo outInfo, boolean writeSettings) {
+    private boolean deleteSystemPackageLI(PackageParser.Package deletedPkg,
+            PackageSetting deletedPs, int[] allUserHandles, boolean[] perUserInstalled,
+            int flags, PackageRemovedInfo outInfo, boolean writeSettings,
+            PackageParser.Package replacingPackage) {
+        if (deletedPkg.parentPackage != null) {
+            Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
+            return false;
+        }
+
         final boolean applyUserRestrictions
                 = (allUserHandles != null) && (perUserInstalled != null);
-        PackageSetting disabledPs = null;
+        final PackageSetting disabledPs;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
         // the system pkg from system partition
         // reader
         synchronized (mPackages) {
-            disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
+            disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPkg.packageName);
         }
-        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + newPs
+
+        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
                 + " disabledPs=" + disabledPs);
+
         if (disabledPs == null) {
-            Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
+            Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
             return false;
         } else if (DEBUG_REMOVE) {
             Slog.d(TAG, "Deleting system pkg from data partition");
         }
+
         if (DEBUG_REMOVE) {
             if (applyUserRestrictions) {
                 Slog.d(TAG, "Remembering install states:");
@@ -13742,27 +14191,30 @@
                 }
             }
         }
+
         // Delete the updated package
         outInfo.isRemovedPackageSystemUpdate = true;
-        if (disabledPs.versionCode < newPs.versionCode) {
+        if (disabledPs.versionCode < deletedPs.versionCode) {
             // Delete data for downgrades
             flags &= ~PackageManager.DELETE_KEEP_DATA;
         } else {
             // Preserve data by setting flag
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
-        boolean ret = deleteInstalledPackageLI(newPs, true, flags,
-                allUserHandles, perUserInstalled, outInfo, writeSettings);
+        boolean ret = deleteInstalledPackageLI(deletedPkg, true, flags, allUserHandles,
+                perUserInstalled, outInfo, writeSettings, replacingPackage);
         if (!ret) {
             return false;
         }
+
         // writer
         synchronized (mPackages) {
             // Reinstate the old system package
-            mSettings.enableSystemPackageLPw(newPs.name);
+            enableSystemPackageLPw(disabledPs.pkg);
             // Remove any native libraries from the upgraded package.
-            NativeLibraryHelper.removeNativeBinariesLI(newPs.legacyNativeLibraryPathString);
+            removeNativeBinariesLI(deletedPkg);
         }
+
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
@@ -13774,7 +14226,8 @@
         try {
             newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
         } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to restore system package " + newPs.name + ": " + e.getMessage());
+            Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+                    + e.getMessage());
             return false;
         }
 
@@ -13787,7 +14240,7 @@
             // Propagate the permissions state as we do not want to drop on the floor
             // runtime permissions. The update permissions method below will take
             // care of removing obsolete permissions and grant install permissions.
-            ps.getPermissionsState().copyFrom(newPs.getPermissionsState());
+            ps.getPermissionsState().copyFrom(deletedPs.getPermissionsState());
             updatePermissionsLPw(newPkg.packageName, newPkg,
                     UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
 
@@ -13816,23 +14269,58 @@
         return true;
     }
 
-    private boolean deleteInstalledPackageLI(PackageSetting ps,
-            boolean deleteCodeAndResources, int flags,
-            int[] allUserHandles, boolean[] perUserInstalled,
-            PackageRemovedInfo outInfo, boolean writeSettings) {
-        if (outInfo != null) {
-            outInfo.uid = ps.appId;
+    private boolean deleteInstalledPackageLI(PackageParser.Package pkg,
+            boolean deleteCodeAndResources, int flags, int[] allUserHandles,
+            boolean[] perUserInstalled, PackageRemovedInfo outInfo, boolean writeSettings,
+            PackageParser.Package replacingPackage) {
+        PackageSetting ps = null;
+
+        synchronized (mPackages) {
+            pkg = mPackages.get(pkg.packageName);
+            if (pkg == null) {
+                return false;
+            }
+
+            ps = mSettings.mPackages.get(pkg.packageName);
+            if (ps == null) {
+                return false;
+            }
+
+            if (outInfo != null) {
+                outInfo.uid = ps.appId;
+            }
         }
 
         // Delete package data from internal structures and also remove data if flag is set
-        removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);
+        removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags,
+                    writeSettings);
 
-        // Delete application code and resources
-        if (deleteCodeAndResources && (outInfo != null)) {
-            outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
-                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
-            if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+        // Delete the child packages data
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageSetting childPs;
+            synchronized (mPackages) {
+                childPs = mSettings.peekPackageLPr(pkg.childPackages.get(i).packageName);
+            }
+            if (childPs != null) {
+                final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0
+                        && (replacingPackage != null
+                        && !replacingPackage.hasChildPackage(childPs.name))
+                        ? flags & ~DELETE_KEEP_DATA : flags;
+                removePackageDataLI(childPs, allUserHandles, perUserInstalled, outInfo,
+                        deleteFlags, writeSettings);
+            }
         }
+
+        // Delete application code and resources only for parent packages
+        if (ps.pkg.parentPackage == null) {
+                if (deleteCodeAndResources && (outInfo != null)) {
+                outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+                        ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
+                if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+            }
+        }
+
         return true;
     }
 
@@ -13898,105 +14386,80 @@
      */
     private boolean deletePackageLI(String packageName, UserHandle user,
             boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
-            int flags, PackageRemovedInfo outInfo,
-            boolean writeSettings) {
+            int flags, PackageRemovedInfo outInfo, boolean writeSettings,
+            PackageParser.Package replacingPackage) {
         if (packageName == null) {
             Slog.w(TAG, "Attempt to delete null packageName.");
             return false;
         }
+
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
+
         PackageSetting ps;
-        boolean dataOnly = false;
         int removeUser = -1;
-        int appId = -1;
+
         synchronized (mPackages) {
             ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
                 return false;
             }
-            if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
-                    && user.getIdentifier() != UserHandle.USER_ALL) {
-                // The caller is asking that the package only be deleted for a single
-                // user.  To do this, we just mark its uninstalled state and delete
-                // its data.  If this is a system app, we only allow this to happen if
-                // they have set the special DELETE_SYSTEM_APP which requests different
-                // semantics than normal for uninstalling system apps.
-                if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
-                final int userId = user.getIdentifier();
-                ps.setUserState(userId,
-                        COMPONENT_ENABLED_STATE_DEFAULT,
-                        false, //installed
-                        true,  //stopped
-                        true,  //notLaunched
-                        false, //hidden
-                        false, //suspended
-                        null, null, null,
-                        false, // blockUninstall
-                        ps.readUserState(userId).domainVerificationStatus, 0);
-                if (!isSystemApp(ps)) {
-                    // Do not uninstall the APK if an app should be cached
-                    boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
-                    if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
-                        // Other user still have this package installed, so all
-                        // we need to do is clear this user's data and save that
-                        // it is uninstalled.
-                        if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
-                        removeUser = user.getIdentifier();
-                        appId = ps.appId;
-                        scheduleWritePackageRestrictionsLocked(removeUser);
-                    } else {
-                        // We need to set it back to 'installed' so the uninstall
-                        // broadcasts will be sent correctly.
-                        if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
-                        ps.setInstalled(true, user.getIdentifier());
-                    }
-                } else {
-                    // This is a system app, so we assume that the
-                    // other users still have this package installed, so all
+
+            if (ps.pkg.parentPackage != null && (!isSystemApp(ps)
+                    || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
+                if (DEBUG_REMOVE) {
+                    Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
+                            + ((user == null) ? UserHandle.USER_ALL : user));
+                }
+                if (!clearPackageStateForUser(ps, removeUser, outInfo)) {
+                    return false;
+                }
+                markPackageUninstalledForUserLPw(ps, user);
+                scheduleWritePackageRestrictionsLocked(user);
+                return true;
+            }
+        }
+
+        if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+                && user.getIdentifier() != UserHandle.USER_ALL)) {
+            // The caller is asking that the package only be deleted for a single
+            // user.  To do this, we just mark its uninstalled state and delete
+            // its data. If this is a system app, we only allow this to happen if
+            // they have set the special DELETE_SYSTEM_APP which requests different
+            // semantics than normal for uninstalling system apps.
+            markPackageUninstalledForUserLPw(ps, user);
+
+            if (!isSystemApp(ps)) {
+                // Do not uninstall the APK if an app should be cached
+                boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
+                if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
+                    // Other user still have this package installed, so all
                     // we need to do is clear this user's data and save that
-                    // it is uninstalled.
-                    if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
-                    removeUser = user.getIdentifier();
-                    appId = ps.appId;
-                    scheduleWritePackageRestrictionsLocked(removeUser);
+                // it is uninstalled.
+                if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
+                    if (!clearPackageStateForUser(ps, removeUser, outInfo)) {
+                        return false;
+                    }
+                    scheduleWritePackageRestrictionsLocked(user);
+                    return true;
+                } else {
+                    // We need to set it back to 'installed' so the uninstall
+                    // broadcasts will be sent correctly.
+                    if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
+                    ps.setInstalled(true, user.getIdentifier());
                 }
-            }
-        }
-
-        if (removeUser >= 0) {
-            // From above, we determined that we are deleting this only
-            // for a single user.  Continue the work here.
-            if (DEBUG_REMOVE) Slog.d(TAG, "Updating install state for user: " + removeUser);
-            if (outInfo != null) {
-                outInfo.removedPackage = packageName;
-                outInfo.removedAppId = appId;
-                outInfo.removedUsers = new int[] {removeUser};
-            }
-            // TODO: triage flags as part of 26466827
-            final int installerFlags = StorageManager.FLAG_STORAGE_CE
-                    | StorageManager.FLAG_STORAGE_DE;
-            try {
-                mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
-            } catch (InstallerException e) {
-                Slog.w(TAG, "Failed to delete app data", e);
-            }
-            removeKeystoreDataIfNeeded(removeUser, appId);
-            schedulePackageCleaning(packageName, removeUser, false);
-            synchronized (mPackages) {
-                if (clearPackagePreferredActivitiesLPw(packageName, removeUser)) {
-                    scheduleWritePackageRestrictionsLocked(removeUser);
+            } else {
+                // This is a system app, so we assume that the
+                // other users still have this package installed, so all
+                // we need to do is clear this user's data and save that
+                // it is uninstalled.
+                if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
+                if (!clearPackageStateForUser(ps, removeUser, outInfo)) {
+                    return false;
                 }
-                resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, removeUser);
+                scheduleWritePackageRestrictionsLocked(user);
+                return true;
             }
-            return true;
-        }
-
-        if (dataOnly) {
-            // Delete application data first
-            if (DEBUG_REMOVE) Slog.d(TAG, "Removing package data only");
-            removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
-            return true;
         }
 
         boolean ret = false;
@@ -14004,21 +14467,70 @@
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources as well and
             // fall back to existing code in system partition
-            ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
-                    flags, outInfo, writeSettings);
+            ret = deleteSystemPackageLI(ps.pkg, ps, allUserHandles, perUserInstalled,
+                    flags, outInfo, writeSettings, replacingPackage);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, ps.appId, "uninstall pkg");
-            ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
-                    allUserHandles, perUserInstalled,
-                    outInfo, writeSettings);
+            ret = deleteInstalledPackageLI(ps.pkg, deleteCodeAndResources, flags, allUserHandles,
+                    perUserInstalled, outInfo, writeSettings, replacingPackage);
         }
 
         return ret;
     }
 
-    private final static class ClearStorageConnection implements ServiceConnection {
+    private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
+        final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
+                ? sUserManager.getUserIds() : new int[] {user.getIdentifier()};
+        for (int nextUserId : userIds) {
+            if (DEBUG_REMOVE) {
+                Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);
+            }
+            ps.setUserState(nextUserId, COMPONENT_ENABLED_STATE_DEFAULT,
+                    false /*installed*/, true /*stopped*/, true /*notLaunched*/,
+                    false /*hidden*/, false /*suspended*/, null, null, null,
+                    false /*blockUninstall*/,
+                    ps.readUserState(nextUserId).domainVerificationStatus, 0);
+        }
+    }
+
+    private boolean clearPackageStateForUser(PackageSetting ps, int userId,
+            PackageRemovedInfo outInfo) {
+        final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+                : new int[] {userId};
+        for (int nextUserId : userIds) {
+            if (DEBUG_REMOVE) {
+                Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+                        + nextUserId);
+            }
+            final int flags =  StorageManager.FLAG_STORAGE_CE|  StorageManager.FLAG_STORAGE_DE;
+            try {
+                mInstaller.destroyAppData(ps.volumeUuid, ps.name, nextUserId, flags);
+            } catch (InstallerException e) {
+                Slog.w(TAG, "Couldn't remove cache files for package " + ps.name, e);
+                return false;
+            }
+            removeKeystoreDataIfNeeded(nextUserId, ps.appId);
+            schedulePackageCleaning(ps.name, nextUserId, false);
+            synchronized (mPackages) {
+                if (clearPackagePreferredActivitiesLPw(ps.name, nextUserId)) {
+                    scheduleWritePackageRestrictionsLocked(nextUserId);
+                }
+                resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
+            }
+        }
+
+        if (outInfo != null) {
+            outInfo.removedPackage = ps.name;
+            outInfo.removedAppId = ps.appId;
+            outInfo.removedUsers = userIds;
+        }
+
+        return true;
+    }
+
+    private final class ClearStorageConnection implements ServiceConnection {
         IMediaContainerService mContainerService;
 
         @Override
@@ -16708,7 +17220,7 @@
 
     /*
      * Unload packages mounted on external media. This involves deleting package
-     * data from internal structures, sending broadcasts about diabled packages,
+     * data from internal structures, sending broadcasts about disabled packages,
      * gc'ing to free up references, unmounting all secure containers
      * corresponding to packages on external media, and posting a
      * UPDATED_MEDIA_STATUS message if status has been requested. Please note
@@ -16730,7 +17242,7 @@
             PackageRemovedInfo outInfo = new PackageRemovedInfo();
             synchronized (mInstallLock) {
                 boolean res = deletePackageLI(pkgName, null, false, null, null,
-                        PackageManager.DELETE_KEEP_DATA, outInfo, false);
+                        PackageManager.DELETE_KEEP_DATA, outInfo, false, null);
                 if (res) {
                     pkgList.add(pkgName);
                 } else {
@@ -16876,7 +17388,7 @@
                 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)) {
+                        PackageManager.DELETE_KEEP_DATA, outInfo, false, null)) {
                     unloaded.add(info);
                 } else {
                     Slog.w(TAG, "Failed to unload " + ps.codePath);
@@ -17121,6 +17633,15 @@
      * left intact.
      */
     private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
+        prepareAppDataAfterInstallInternal(pkg);
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            PackageParser.Package childPackage = pkg.childPackages.get(i);
+            prepareAppDataAfterInstallInternal(childPackage);
+        }
+    }
+
+    private void prepareAppDataAfterInstallInternal(PackageParser.Package pkg) {
         final PackageSetting ps;
         synchronized (mPackages) {
             ps = mSettings.mPackages.get(pkg.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index f106b62..e3866df 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageParser;
 
 import java.io.File;
+import java.util.List;
 
 /**
  * Settings data for a particular package we know about.
@@ -33,10 +34,11 @@
     PackageSetting(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int privateFlags) {
+            int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
+            List<String> childPackageNames) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                pVersionCode, pkgFlags, privateFlags);
+                pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 1117988..e5eec7e 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -28,6 +28,8 @@
 import android.util.SparseArray;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Settings base class for pending and resolved classes.
@@ -49,6 +51,9 @@
     final String name;
     final String realName;
 
+    String parentPackageName;
+    List<String> childPackageNames;
+
     /**
      * Path where this package was found on disk. For monolithic packages
      * this is path to single base APK file; for cluster packages this is
@@ -126,10 +131,14 @@
     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int pkgPrivateFlags) {
+            int pVersionCode, int pkgFlags, int pkgPrivateFlags,
+            String parentPackageName, List<String> childPackageNames) {
         super(pkgFlags, pkgPrivateFlags);
         this.name = name;
         this.realName = realName;
+        this.parentPackageName = parentPackageName;
+        this.childPackageNames = (childPackageNames != null)
+                ? new ArrayList<>(childPackageNames) : null;
         init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
                 secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
     }
@@ -174,6 +183,10 @@
         volumeUuid = base.volumeUuid;
 
         keySetData = new PackageKeySetData(base.keySetData);
+
+        parentPackageName = base.parentPackageName;
+        childPackageNames = (base.childPackageNames != null)
+                ? new ArrayList<>(base.childPackageNames) : null;
     }
 
     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 9a20be7..f5c81e4 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -195,7 +195,7 @@
             for (int i=0; i<mSignatures.length; i++) {
                 if (i > 0) buf.append(", ");
                 buf.append(Integer.toHexString(
-                        System.identityHashCode(mSignatures[i])));
+                        mSignatures[i].hashCode()));
             }
         }
         buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/PendingPackage.java b/services/core/java/com/android/server/pm/PendingPackage.java
index bb0dba1..da73085 100644
--- a/services/core/java/com/android/server/pm/PendingPackage.java
+++ b/services/core/java/com/android/server/pm/PendingPackage.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import java.io.File;
+import java.util.List;
 
 final class PendingPackage extends PackageSettingBase {
     final int sharedId;
@@ -24,10 +25,11 @@
     PendingPackage(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int sharedId,
-            int pVersionCode, int pkgFlags, int pkgPrivateFlags) {
+            int pVersionCode, int pkgFlags, int pkgPrivateFlags, String parentPackageName,
+            List<String> childPackageNames) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                pVersionCode, pkgFlags, pkgPrivateFlags);
+                pVersionCode, pkgFlags, pkgPrivateFlags, parentPackageName, childPackageNames);
         this.sharedId = sharedId;
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fcb777b..c1a5c5a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -184,6 +184,8 @@
     private static final String TAG_SHARED_USER = "shared-user";
     private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
     private static final String TAG_PERMISSIONS = "perms";
+    private static final String TAG_CHILD_PACKAGE = "child-package";
+
     private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
             "persistent-preferred-activities";
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
@@ -416,9 +418,23 @@
             String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi,
             int pkgFlags, int pkgPrivateFlags, UserHandle user, boolean add) {
         final String name = pkg.packageName;
+        final String parentPackageName = (pkg.parentPackage != null)
+                ? pkg.parentPackage.packageName : null;
+
+        List<String> childPackageNames = null;
+        if (pkg.childPackages != null) {
+            final int childCount = pkg.childPackages.size();
+            childPackageNames = new ArrayList<>(childCount);
+            for (int i = 0; i < childCount; i++) {
+                String childPackageName = pkg.childPackages.get(i).packageName;
+                childPackageNames.add(childPackageName);
+            }
+        }
+
         PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
                 resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, secondaryCpuAbi,
-                pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /* allowInstall */);
+                pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /* allowInstall */,
+                parentPackageName, childPackageNames);
         return p;
     }
 
@@ -503,8 +519,7 @@
         return mSharedUsers.values();
     }
 
-
-    boolean disableSystemPackageLPw(String name) {
+    boolean disableSystemPackageLPw(String name, boolean replaced) {
         final PackageSetting p = mPackages.get(name);
         if(p == null) {
             Log.w(PackageManagerService.TAG, "Package " + name + " is not an installed package");
@@ -512,18 +527,22 @@
         }
         final PackageSetting dp = mDisabledSysPackages.get(name);
         // always make sure the system package code and resource paths dont change
-        if (dp == null) {
+        if (dp == null && p.pkg != null && p.pkg.isSystemApp() && !p.pkg.isUpdatedSystemApp()) {
             if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
                 p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
             mDisabledSysPackages.put(name, p);
 
-            // a little trick...  when we install the new package, we don't
-            // want to modify the existing PackageSetting for the built-in
-            // version.  so at this point we need a new PackageSetting that
-            // is okay to muck with.
-            PackageSetting newp = new PackageSetting(p);
-            replacePackageLPw(name, newp);
+            if (replaced) {
+                // a little trick...  when we install the new package, we don't
+                // want to modify the existing PackageSetting for the built-in
+                // version.  so at this point we need a new PackageSetting that
+                // is okay to muck with.
+                PackageSetting newp = new PackageSetting(p);
+                replacePackageLPw(name, newp);
+            } else {
+                mPackages.remove(name);
+            }
             return true;
         }
         return false;
@@ -542,7 +561,8 @@
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.secondaryCpuAbiString,
-                p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags);
+                p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
+                p.parentPackageName, p.childPackageNames);
         mDisabledSysPackages.remove(name);
         return ret;
     }
@@ -557,7 +577,8 @@
 
     PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
-            String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags) {
+            String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags,
+            String parentPackageName, List<String> childPackageNames) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (p.appId == uid) {
@@ -569,7 +590,8 @@
         }
         p = new PackageSetting(name, realName, codePath, resourcePath,
                 legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags);
+                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
+                childPackageNames);
         p.appId = uid;
         if (addUserIdLPw(uid, p, name)) {
             mPackages.put(name, p);
@@ -650,7 +672,8 @@
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
-            UserHandle installUser, boolean add, boolean allowInstall) {
+            UserHandle installUser, boolean add, boolean allowInstall, String parentPackage,
+            List<String> childPackageNames) {
         PackageSetting p = mPackages.get(name);
         UserManagerService userManager = UserManagerService.getInstance();
         if (p != null) {
@@ -700,7 +723,8 @@
                 // We are consuming the data from an existing package.
                 p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
                         legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                        null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
+                        null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,
+                        parentPackage, childPackageNames);
                 if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                         + name + " is adopting original package " + origPackage.name);
                 // Note that we will retain the new package's signature so
@@ -719,7 +743,8 @@
             } else {
                 p = new PackageSetting(name, realName, codePath, resourcePath,
                         legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                        null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
+                        null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,
+                        parentPackage, childPackageNames);
                 p.setTimeStamp(codePath.lastModified());
                 p.sharedUser = sharedUser;
                 // If this is not a system app, it starts out stopped.
@@ -2049,6 +2074,20 @@
         serializer.endTag(null, TAG_PERMISSIONS);
     }
 
+    void writeChildPackagesLPw(XmlSerializer serializer, List<String> childPackageNames)
+            throws IOException {
+        if (childPackageNames == null) {
+            return;
+        }
+        final int childCount = childPackageNames.size();
+        for (int i = 0; i < childCount; i++) {
+            String childPackageName = childPackageNames.get(i);
+            serializer.startTag(null, TAG_CHILD_PACKAGE);
+            serializer.attribute(null, ATTR_NAME, childPackageName);
+            serializer.endTag(null, TAG_CHILD_PACKAGE);
+        }
+    }
+
     // Note: assumed "stopped" field is already cleared in all packages.
     // Legacy reader, used to read in the old file format after an upgrade. Not used after that.
     void readStoppedLPw() {
@@ -2359,7 +2398,7 @@
                 }
 
                 final ApplicationInfo ai = pkg.pkg.applicationInfo;
-                final String dataPath = new File(ai.dataDir).getCanonicalPath();
+                final String dataPath = ai.dataDir;
                 final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                 final int[] gids = pkg.getPermissionsState().computeGids(userIds);
 
@@ -2449,6 +2488,12 @@
             serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
         }
 
+        if (pkg.parentPackageName != null) {
+            serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
+        }
+
+        writeChildPackagesLPw(serializer, pkg.childPackageNames);
+
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
             writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -2506,6 +2551,13 @@
         if (pkg.volumeUuid != null) {
             serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
         }
+
+        if (pkg.parentPackageName != null) {
+            serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
+        }
+
+        writeChildPackagesLPw(serializer, pkg.childPackageNames);
+
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
 
         writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -2798,7 +2850,8 @@
                         (SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
                         pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString,
                         pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, pp.pkgPrivateFlags,
-                        null, true /* add */, false /* allowInstall */);
+                        null, true /* add */, false /* allowInstall */, pp.parentPackageName,
+                        pp.childPackageNames);
                 if (p == null) {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unable to create application package for " + pp.name);
@@ -3246,6 +3299,8 @@
         String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi");
         String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
 
+        String parentPackageName = parser.getAttributeValue(null, "parentPackageName");
+
         String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi");
         String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi");
         String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride");
@@ -3275,7 +3330,8 @@
         }
         PackageSetting ps = new PackageSetting(name, realName, codePathFile,
                 new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
-                secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags);
+                secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
+                parentPackageName, null);
         String timeStampStr = parser.getAttributeValue(null, "ft");
         if (timeStampStr != null) {
             try {
@@ -3324,6 +3380,12 @@
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
                 readInstallPermissionsLPr(parser, ps.getPermissionsState());
+            } else if (parser.getName().equals(TAG_CHILD_PACKAGE)) {
+                String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
+                if (ps.childPackageNames == null) {
+                    ps.childPackageNames = new ArrayList<>();
+                }
+                ps.childPackageNames.add(childPackageName);
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <updated-package>: " + parser.getName());
@@ -3363,6 +3425,7 @@
         PackageSettingBase packageSetting = null;
         String version = null;
         int versionCode = 0;
+        String parentPackageName;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
@@ -3374,6 +3437,8 @@
 
             legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
 
+            parentPackageName = parser.getAttributeValue(null, "parentPackageName");
+
             legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
             primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
             secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
@@ -3494,7 +3559,7 @@
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                         new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
                         secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
-                        pkgPrivateFlags);
+                        pkgPrivateFlags, parentPackageName, null);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                             + userId + " pkg=" + packageSetting);
@@ -3513,7 +3578,8 @@
                     packageSetting = new PendingPackage(name.intern(), realName, new File(
                             codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                            userId, versionCode, pkgFlags, pkgPrivateFlags);
+                            userId, versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
+                            null);
                     packageSetting.setTimeStamp(timeStamp);
                     packageSetting.firstInstallTime = firstInstallTime;
                     packageSetting.lastUpdateTime = lastUpdateTime;
@@ -3575,6 +3641,7 @@
                     packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
                 }
             }
+
             int outerDepth = parser.getDepth();
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3621,6 +3688,12 @@
                     packageSetting.keySetData.addDefinedKeySet(id, alias);
                 } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
                     readDomainVerificationLPw(parser, packageSetting);
+                } else if (tagName.equals(TAG_CHILD_PACKAGE)) {
+                    String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
+                    if (packageSetting.childPackageNames == null) {
+                        packageSetting.childPackageNames = new ArrayList<>();
+                    }
+                    packageSetting.childPackageNames.add(childPackageName);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <package>: " + parser.getName());
@@ -3884,6 +3957,28 @@
         return mVerifierDeviceIdentity;
     }
 
+    public boolean hasOtherDisabledSystemPkgWithChildLPr(String parentPackageName,
+            String childPackageName) {
+        final int packageCount = mDisabledSysPackages.size();
+        for (int i = 0; i < packageCount; i++) {
+            PackageSetting disabledPs = mDisabledSysPackages.valueAt(i);
+            if (disabledPs.childPackageNames == null || disabledPs.childPackageNames.isEmpty()) {
+                continue;
+            }
+            if (disabledPs.name.equals(parentPackageName)) {
+                continue;
+            }
+            final int childCount = disabledPs.childPackageNames.size();
+            for (int j = 0; j < childCount; j++) {
+                String currChildPackageName = disabledPs.childPackageNames.get(j);
+                if (currChildPackageName.equals(childPackageName)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public PackageSetting getDisabledSystemPkgLPr(String name) {
         PackageSetting ps = mDisabledSysPackages.get(name);
         return ps;
@@ -4136,6 +4231,34 @@
         }
         pw.println();
         if (ps.pkg != null) {
+            if (ps.pkg.parentPackage != null) {
+                PackageParser.Package parentPkg = ps.pkg.parentPackage;
+                PackageSetting pps = mPackages.get(parentPkg.packageName);
+                if (pps == null || !pps.codePathString.equals(parentPkg.codePath)) {
+                    pps = mDisabledSysPackages.get(parentPkg.packageName);
+                }
+                if (pps != null) {
+                    pw.print(prefix); pw.print("  parentPackage=");
+                    pw.println(pps.realName != null ? pps.realName : pps.name);
+                }
+            } else if (ps.pkg.childPackages != null) {
+                pw.print(prefix); pw.print("  childPackages=[");
+                final int childCount = ps.pkg.childPackages.size();
+                for (int i = 0; i < childCount; i++) {
+                    PackageParser.Package childPkg = ps.pkg.childPackages.get(i);
+                    PackageSetting cps = mPackages.get(childPkg.packageName);
+                    if (cps == null || !cps.codePathString.equals(childPkg.codePath)) {
+                        cps = mDisabledSysPackages.get(childPkg.packageName);
+                    }
+                    if (cps != null) {
+                        if (i > 0) {
+                            pw.print(", ");
+                        }
+                        pw.print(cps.realName != null ? cps.realName : cps.name);
+                    }
+                }
+                pw.println("]");
+            }
             pw.print(prefix); pw.print("  versionName="); pw.println(ps.pkg.mVersionName);
             pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, ps.pkg); pw.println();
             pw.print(prefix); pw.print("  applicationInfo=");
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 342c078..3e99a4c 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -330,12 +330,12 @@
             if (DEBUG) {
                 Slog.d(TAG, "add " + info.getId());
             }
-            TvInputState state = userState.inputMap.get(info.getId());
-            if (state == null) {
-                state = new TvInputState();
+            TvInputState inputState = userState.inputMap.get(info.getId());
+            if (inputState == null) {
+                inputState = new TvInputState();
+                inputState.info = info;
             }
-            state.info = info;
-            inputMap.put(info.getId(), state);
+            inputMap.put(info.getId(), inputState);
         }
 
         for (String inputId : inputMap.keySet()) {
@@ -781,7 +781,17 @@
         if (DEBUG) {
             Slog.d(TAG, "setTvInputInfoLocked(inputInfo=" + inputInfo + ")");
         }
-        // TODO: Also update the internal input list.
+        String inputId = inputInfo.getId();
+        TvInputState inputState = userState.inputMap.get(inputId);
+        if (inputState == null) {
+            Slog.e(TAG, "failed to set input info - unknown input id " + inputId);
+            return;
+        }
+        if (inputState.info.equals(inputInfo)) {
+            return;
+        }
+        inputState.info = inputInfo;
+
         for (ITvInputManagerCallback callback : userState.callbackSet) {
             try {
                 callback.onTvInputInfoChanged(inputInfo);
@@ -852,7 +862,7 @@
             }
 
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "setTvInputInfoChanged");
+                    Binder.getCallingUid(), userId, "setTvInputInfo");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7ec945d..93b1d62 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -127,6 +127,8 @@
 
     boolean mAlwaysFocusable;
 
+    boolean mAppStopped;
+
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
 
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
@@ -311,6 +313,47 @@
         }
     }
 
+    // Here we destroy surfaces which have been marked as eligible by the animator, taking care
+    // to ensure the client has finished with them. If the client could still be using them
+    // we will skip destruction and try again when the client has stopped.
+    void destroySurfaces() {
+        final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
+        final DisplayContentList displayList = new DisplayContentList();
+        for (int i = allWindows.size() - 1; i >= 0; i--) {
+            final WindowState win = allWindows.get(i);
+            if (!win.mDestroying) {
+                continue;
+            }
+
+            if (!mAppStopped && !win.mClientRemoveRequested) {
+                return;
+            }
+
+            win.destroyOrSaveSurface();
+            if (win.mRemoveOnExit) {
+                win.mExiting = false;
+                service.removeWindowInnerLocked(win);
+            }
+            final DisplayContent displayContent = win.getDisplayContent();
+            if (displayContent != null && !displayList.contains(displayContent)) {
+                displayList.add(displayContent);
+            }
+            win.mDestroying = false;
+        }
+        for (int i = 0; i < displayList.size(); i++) {
+            final DisplayContent displayContent = displayList.get(i);
+            service.mLayersController.assignLayersLocked(displayContent.getWindowList());
+            displayContent.layoutNeeded = true;
+        }
+    }
+
+    // The application has stopped, so destroy any surfaces which were keeping alive
+    // in case they were still being used.
+    void notifyAppStopped() {
+        mAppStopped = true;
+        destroySurfaces();
+    }
+
     /**
      * Checks whether we should save surfaces for this app.
      *
@@ -513,6 +556,9 @@
         mFrozenBounds.remove();
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
+            if (!win.mHasSurface) {
+                continue;
+            }
             win.mLayoutNeeded = true;
             win.setDisplayLayoutNeeded();
             if (!service.mResizingWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 1bfdcce..0678ca2 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -22,7 +22,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.graphics.Rect;
 import android.util.ArrayMap;
@@ -41,8 +40,9 @@
  */
 public class BoundsAnimationController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BoundsAnimationController" : TAG_WM;
+    private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
 
-    // Only acccessed on UI thread.
+    // Only accessed on UI thread.
     private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
 
     private final class BoundsAnimator extends ValueAnimator
@@ -52,14 +52,22 @@
         private final Rect mTo;
         private final Rect mTmpRect;
         private final boolean mMoveToFullScreen;
+        // True if this this animation was cancelled and will be replaced the another animation from
+        // the same {@link #AnimateBoundsUser} target.
+        private boolean mWillReplace;
+        // True to true if this animation replaced a previous animation of the same
+        // {@link #AnimateBoundsUser} target.
+        private final boolean mReplacement;
 
-        BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen) {
+        BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to,
+                boolean moveToFullScreen, boolean replacement) {
             super();
             mTarget = target;
             mFrom = from;
             mTo = to;
             mTmpRect = new Rect();
             mMoveToFullScreen = moveToFullScreen;
+            mReplacement = replacement;
             addUpdateListener(this);
             addListener(this);
         }
@@ -68,10 +76,10 @@
         public void onAnimationUpdate(ValueAnimator animation) {
             final float value = (Float) animation.getAnimatedValue();
             final float remains = 1 - value;
-            mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value);
-            mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value);
-            mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value);
-            mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value);
+            mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
+            mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
+            mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
+            mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
             if (DEBUG_ANIM) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + ", mBounds="
                     + mTmpRect + ", from=" + mFrom + ", mTo=" + mTo + ", value=" + value
                     + ", remains=" + remains);
@@ -85,13 +93,15 @@
 
         @Override
         public void onAnimationStart(Animator animation) {
-
+            if (!mReplacement) {
+                mTarget.onAnimationStart();
+            }
         }
 
         @Override
         public void onAnimationEnd(Animator animation) {
             finishAnimation();
-            if (mMoveToFullScreen) {
+            if (mMoveToFullScreen && !mWillReplace) {
                 mTarget.moveToFullscreen();
             }
         }
@@ -101,8 +111,16 @@
             finishAnimation();
         }
 
+        @Override
+        public void cancel() {
+            mWillReplace = true;
+            super.cancel();
+        }
+
         private void finishAnimation() {
-            mTarget.finishBoundsAnimation();
+            if (!mWillReplace) {
+                mTarget.onAnimationEnd();
+            }
             removeListener(this);
             removeUpdateListener(this);
             mRunningAnimations.remove(mTarget);
@@ -126,11 +144,13 @@
          */
         boolean setSize(Rect bounds);
 
+        void onAnimationStart();
+
         /**
-         * Callback for the target to inform it that the animation is finished, so it can do some
+         * Callback for the target to inform it that the animation has ended, so it can do some
          * necessary cleanup.
          */
-        void finishBoundsAnimation();
+        void onAnimationEnd();
 
         void moveToFullscreen();
 
@@ -146,13 +166,15 @@
         }
 
         final BoundsAnimator existing = mRunningAnimations.get(target);
-        if (existing != null) {
+        final boolean replacing = existing != null;
+        if (replacing) {
             existing.cancel();
         }
-        BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen);
+        final BoundsAnimator animator =
+                new BoundsAnimator(target, from, to, moveToFullscreen, replacing);
         mRunningAnimations.put(target, animator);
         animator.setFloatValues(0f, 1f);
-        animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+        animator.setDuration(DEFAULT_APP_TRANSITION_DURATION * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
         animator.setInterpolator(new LinearInterpolator());
         animator.start();
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a06d3fc..4167ac4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -540,7 +540,7 @@
     }
 
     boolean isDragResizing() {
-        return mDragResizing;
+        return mDragResizing || (mStack != null && mStack.isDragResizing());
     }
 
     void updateDisplayInfo(final DisplayContent displayContent) {
@@ -584,7 +584,7 @@
             final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                 final WindowState win = windows.get(winNdx);
-                if (!resizingWindows.contains(win)) {
+                if (win.mHasSurface && !resizingWindows.contains(win)) {
                     if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
                     resizingWindows.add(win);
                 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 40ca1c5..a8b72892 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -99,6 +99,9 @@
     boolean mDeferDetach;
     private boolean mUpdateBoundsAfterRotation = false;
 
+    // Whether the stack and all its tasks is currently being drag-resized
+    private boolean mDragResizing;
+
     TaskStack(WindowManagerService service, int stackId) {
         mService = service;
         mStackId = stackId;
@@ -911,6 +914,10 @@
         return false;
     }
 
+    boolean isDragResizing() {
+        return mDragResizing;
+    }
+
     @Override  // AnimatesBounds
     public boolean setSize(Rect bounds) {
         synchronized (mService.mWindowMap) {
@@ -926,16 +933,17 @@
     }
 
     @Override  // AnimatesBounds
-    public void finishBoundsAnimation() {
+    public void onAnimationStart() {
         synchronized (mService.mWindowMap) {
-            if (mTasks.isEmpty()) {
-                return;
-            }
-            final Task task = mTasks.get(mTasks.size() - 1);
-            if (task != null) {
-                task.setDragResizing(false);
-                mService.requestTraversal();
-            }
+            mDragResizing = true;
+        }
+    }
+
+    @Override  // AnimatesBounds
+    public void onAnimationEnd() {
+        synchronized (mService.mWindowMap) {
+            mDragResizing = false;
+            mService.requestTraversal();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae6c89a..1b041cb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2154,6 +2154,14 @@
             if (win == null) {
                 return;
             }
+            // We set this here instead of removeWindowLocked because we only want it to be
+            // true when the client has requested we remove the window. In other remove
+            // cases, we have to wait for activity stop to safely remove the window (as the
+            // client may still be using the surface). In this case though, the client has
+            // just dismissed a window (for example a Dialog) and activity stop isn't
+            // necessarily imminent, so we need to know not to wait for it after our
+            // hanimation (if applicable) finishes.
+            win.mClientRemoveRequested = true;
             removeWindowLocked(win);
         }
     }
@@ -4188,6 +4196,24 @@
     }
 
     @Override
+    public void notifyAppStopped(IBinder token) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "notifyAppStopped()")) {
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+        }
+
+        synchronized(mWindowMap) {
+            final AppWindowToken wtoken;
+            wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+                return;
+            }
+            wtoken.notifyAppStopped();
+        }
+    }
+
+    @Override
     public void setAppVisibility(IBinder token, boolean visible) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "setAppVisibility()")) {
@@ -4210,6 +4236,7 @@
 
             mOpeningApps.remove(wtoken);
             mClosingApps.remove(wtoken);
+            wtoken.mAppStopped = false;
             wtoken.waitingToShow = false;
             wtoken.hiddenRequested = !visible;
 
@@ -10350,13 +10377,6 @@
                 Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
                 return;
             }
-            final ArrayList<Task> tasks = stack.getTasks();
-            if (tasks.isEmpty()) {
-                Slog.w(TAG, "animateResizePinnedStack: pinned stack doesn't have any tasks.");
-                return;
-            }
-            final Task task = tasks.get(tasks.size() - 1);
-            task.setDragResizing(true);
             final Rect originalBounds = new Rect();
             stack.getBounds(originalBounds);
             UiThread.getHandler().post(new Runnable() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 880514c..3430b34 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -386,6 +386,13 @@
     boolean mRemoved;
 
     /**
+     * Has the client requested we remove the window? In this case we know
+     * that we can dispose of it when we wish without further synchronization
+     * with the client
+     */
+    boolean mClientRemoveRequested;
+
+    /**
      * Temp for keeping track of windows that have been removed when
      * rebuilding window list.
      */
@@ -2145,7 +2152,7 @@
         // background.
         return (mDisplayContent.mDividerControllerLocked.isResizing()
                         || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
-                !task.inFreeformWorkspace();
+                !task.inFreeformWorkspace() && isVisibleLw();
 
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c7c9cbf..0201296 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -471,16 +471,31 @@
         if (WindowManagerService.localLOGV) Slog.v(
                 TAG, "Exit animation finished in " + this
                 + ": remove=" + mWin.mRemoveOnExit);
-        if (hasSurface()) {
-            mService.mDestroySurface.add(mWin);
-            mWin.mDestroying = true;
-            hide("finishExit");
+
+
+        mWin.mDestroying = true;
+
+        // If we have an app token, we ask it to destroy the surface for us,
+        // so that it can take care to ensure the activity has actually stopped
+        // and the surface is not still in use. Otherwise we add the service to
+        // mDestroySurface and allow it to be processed in our next transaction.
+        if (mWin.mAppToken != null) {
+            if (hasSurface()) {
+                hide("finishExit");
+            }
+            mWin.mAppToken.destroySurfaces();
+        } else {
+            if (hasSurface()) {
+                mService.mDestroySurface.add(mWin);
+                hide("finishExit");
+            }
+            mWin.mExiting = false;
+            if (mWin.mRemoveOnExit) {
+                mService.mPendingRemove.add(mWin);
+                mWin.mRemoveOnExit = false;
+            }
         }
-        mWin.mExiting = false;
-        if (mWin.mRemoveOnExit) {
-            mService.mPendingRemove.add(mWin);
-            mWin.mRemoveOnExit = false;
-        }
+
         mWallpaperControllerLocked.hideWallpapers(mWin);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 7f9a0de..ba83be1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -39,7 +39,7 @@
     public PackageSetting generateFakePackageSetting(String name) {
         return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
                 new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
-                "", 1, 0, 0);
+                "", 1, 0, 0, null, null);
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2560c31..8d1b124 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -348,6 +348,11 @@
     }
 
     @Override
+    public void notifyAppStopped(IBinder token) throws RemoteException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void setEventDispatching(boolean arg0) throws RemoteException {
         // TODO Auto-generated method stub
     }