Merge changes from topic 'tethering-ownership' into nyc-mr1-dev

* changes:
  Tethering: Own WiFi tethering state and lifetime
  Use an ArrayMap instead of HashMap to track tether interfaces
  Consolidate cleanup logic in TetherInterfaceSM.TetheredState
  Annotate TetherInterfaceStateMachineTest for APCT
  Rely on Tethering mutex for TetherInterfaceSM
  Rename TetherInterfaceSM to TetherInterfaceStateMachine
  Remove dead code from TetherInterfaceSM
  Remove transient StartingState from TetherInterfaceSM
  Expand test coverage of TetherInterfaceSM
  Add demonstration unittest for TetherInterfaceSM
  Extract TetherInterfaceSM to its own class.
  Make Tethering.TetherInterfaceSM more self contained
  Clean up class members in Tethering.TetherInterfaceSM
  Fix trivial warnings in Tethering.java
diff --git a/api/current.txt b/api/current.txt
index 64eff6b..cb42b86 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10088,19 +10088,7 @@
     method public boolean isManifestShortcut();
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
-    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
-    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
-    field public static final int FLAG_DISABLED = 64; // 0x40
-    field public static final int FLAG_DYNAMIC = 1; // 0x1
-    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
-    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
-    field public static final int FLAG_IMMUTABLE = 256; // 0x100
-    field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
-    field public static final int FLAG_MANIFEST = 32; // 0x20
-    field public static final int FLAG_PINNED = 2; // 0x2
-    field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -36144,6 +36132,7 @@
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_WIFI = 8; // 0x8
@@ -36330,6 +36319,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index bdf8299..98b66c0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -169,6 +169,7 @@
     field public static final java.lang.String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS";
     field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS";
     field public static final java.lang.String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY";
+    field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
@@ -10511,19 +10512,7 @@
     method public boolean isManifestShortcut();
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
-    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
-    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
-    field public static final int FLAG_DISABLED = 64; // 0x40
-    field public static final int FLAG_DYNAMIC = 1; // 0x1
-    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
-    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
-    field public static final int FLAG_IMMUTABLE = 256; // 0x100
-    field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
-    field public static final int FLAG_MANIFEST = 32; // 0x20
-    field public static final int FLAG_PINNED = 2; // 0x2
-    field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -38980,6 +38969,7 @@
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_WIFI = 8; // 0x8
@@ -39177,6 +39167,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 93b7368..d20f87a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10101,19 +10101,7 @@
     method public boolean isManifestShortcut();
     method public boolean isPinned();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
-    field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
-    field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
-    field public static final int FLAG_DISABLED = 64; // 0x40
-    field public static final int FLAG_DYNAMIC = 1; // 0x1
-    field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
-    field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
-    field public static final int FLAG_IMMUTABLE = 256; // 0x100
-    field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
-    field public static final int FLAG_MANIFEST = 32; // 0x20
-    field public static final int FLAG_PINNED = 2; // 0x2
-    field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
     field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
   }
 
@@ -36222,6 +36210,7 @@
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
     field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
     field public static final int PROPERTY_WIFI = 8; // 0x8
@@ -36408,6 +36397,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ac5f3ef..890e67d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4219,6 +4219,7 @@
     public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
             @Nullable Bundle options) {
         if (mParent == null) {
+            options = transferSpringboardActivityOptions(options);
             Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivity(
                     this, mMainThread.getApplicationThread(), mToken, this,
@@ -4267,6 +4268,16 @@
         }
     }
 
+    private Bundle transferSpringboardActivityOptions(Bundle options) {
+        if (options == null && (mWindow != null && !mWindow.isActive())) {
+            final ActivityOptions activityOptions = getActivityOptions();
+            if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+                return activityOptions.toBundle();
+            }
+        }
+        return options;
+    }
+
     /**
      * @hide Implement to provide correct calling token.
      */
@@ -4282,6 +4293,7 @@
         if (mParent != null) {
             throw new RuntimeException("Can't be called from a child");
         }
+        options = transferSpringboardActivityOptions(options);
         Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode,
                 options, user);
@@ -4317,6 +4329,7 @@
         if (mParent != null) {
             throw new RuntimeException("Can't be called from a child");
         }
+        options = transferSpringboardActivityOptions(options);
         Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivity(
                         this, mMainThread.getApplicationThread(), mToken, this,
@@ -4349,6 +4362,7 @@
         if (mParent != null) {
             throw new RuntimeException("Can't be called from a child");
         }
+        options = transferSpringboardActivityOptions(options);
         Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivityAsCaller(
                         this, mMainThread.getApplicationThread(), mToken, this,
@@ -4788,6 +4802,7 @@
      */
     public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
             int requestCode, @Nullable Bundle options) {
+        options = transferSpringboardActivityOptions(options);
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, child,
@@ -4853,6 +4868,7 @@
         if (referrer != null) {
             intent.putExtra(Intent.EXTRA_REFERRER, referrer);
         }
+        options = transferSpringboardActivityOptions(options);
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, who,
@@ -5952,7 +5968,7 @@
             return false;
         }
         try {
-            return ActivityManagerNative.getDefault().isTopOfTask(mToken);
+            return ActivityManagerNative.getDefault().isTopOfTask(getActivityToken());
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4c8ddc7..ccc37d7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -31,10 +31,13 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.transition.Transition;
+import android.transition.TransitionManager;
 import android.util.Pair;
 import android.util.Slog;
 import android.view.AppTransitionAnimationSpec;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
 
 import java.util.ArrayList;
@@ -640,10 +643,71 @@
     public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
             Pair<View, String>... sharedElements) {
         ActivityOptions opts = new ActivityOptions();
-        if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
-            opts.mAnimationType = ANIM_DEFAULT;
+        makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
+                activity.mExitTransitionListener, sharedElements);
+        return opts;
+    }
+
+    /**
+     * Call this immediately prior to startActivity to begin a shared element transition
+     * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS.
+     * The exit transition will start immediately and the shared element transition will
+     * start once the launched Activity's shared element is ready.
+     * <p>
+     * When all transitions have completed and the shared element has been transfered,
+     * the window's decor View will have its visibility set to View.GONE.
+     *
+     * @hide
+     */
+    @SafeVarargs
+    public static ActivityOptions startSharedElementAnimation(Window window,
+            Pair<View, String>... sharedElements) {
+        ActivityOptions opts = new ActivityOptions();
+        final View decorView = window.getDecorView();
+        if (decorView == null) {
             return opts;
         }
+        final ExitTransitionCoordinator exit =
+                makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
+        if (exit != null) {
+            HideWindowListener listener = new HideWindowListener(window, exit);
+            exit.setHideSharedElementsCallback(listener);
+            exit.startExit();
+        }
+        return opts;
+    }
+
+    /**
+     * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
+     * animation must be stopped and the Views reset. This can happen if there was an error
+     * from startActivity or a springboard activity and the animation should stop and reset.
+     *
+     * @hide
+     */
+    public static void stopSharedElementAnimation(Window window) {
+        final View decorView = window.getDecorView();
+        if (decorView == null) {
+            return;
+        }
+        final ExitTransitionCoordinator exit = (ExitTransitionCoordinator)
+                decorView.getTag(com.android.internal.R.id.cross_task_transition);
+        if (exit != null) {
+            exit.cancelPendingTransitions();
+            decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null);
+            TransitionManager.endTransitions((ViewGroup) decorView);
+            exit.resetViews();
+            exit.clearState();
+            decorView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
+            ActivityOptions opts, SharedElementCallback callback,
+            Pair<View, String>[] sharedElements) {
+        if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+            opts.mAnimationType = ANIM_DEFAULT;
+            return null;
+        }
         opts.mAnimationType = ANIM_SCENE_TRANSITION;
 
         ArrayList<String> names = new ArrayList<String>();
@@ -665,18 +729,22 @@
             }
         }
 
-        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
-                views, false);
+        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
+                callback, names, names, views, false);
         opts.mTransitionReceiver = exit;
         opts.mSharedElementNames = names;
-        opts.mIsReturning = false;
-        opts.mExitCoordinatorIndex =
-                activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
-        return opts;
+        opts.mIsReturning = (activity == null);
+        if (activity == null) {
+            opts.mExitCoordinatorIndex = -1;
+        } else {
+            opts.mExitCoordinatorIndex =
+                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
+        }
+        return exit;
     }
 
     /** @hide */
-    public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
+    static ActivityOptions makeSceneTransitionAnimation(Activity activity,
             ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
             int resultCode, Intent resultData) {
         ActivityOptions opts = new ActivityOptions();
@@ -900,6 +968,16 @@
         return mIsReturning;
     }
 
+    /**
+     * Returns whether or not the ActivityOptions was created with
+     * {@link #startSharedElementAnimation(Window, Pair[])}.
+     *
+     * @hide
+     */
+    boolean isCrossTask() {
+        return mExitCoordinatorIndex < 0;
+    }
+
     /** @hide */
     public ArrayList<String> getSharedElementNames() {
         return mSharedElementNames;
@@ -1191,4 +1269,65 @@
                 + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="
                 + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
     }
+
+    private static class HideWindowListener extends Transition.TransitionListenerAdapter
+        implements ExitTransitionCoordinator.HideSharedElementsCallback {
+        private final Window mWindow;
+        private final ExitTransitionCoordinator mExit;
+        private final boolean mWaitingForTransition;
+        private boolean mTransitionEnded;
+        private boolean mSharedElementHidden;
+        private ArrayList<View> mSharedElements;
+
+        public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
+            mWindow = window;
+            mExit = exit;
+            mSharedElements = new ArrayList<>(exit.mSharedElements);
+            Transition transition = mWindow.getExitTransition();
+            if (transition != null) {
+                transition.addListener(this);
+                mWaitingForTransition = true;
+            } else {
+                mWaitingForTransition = false;
+            }
+            View decorView = mWindow.getDecorView();
+            if (decorView != null) {
+                if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
+                    throw new IllegalStateException(
+                            "Cannot start a transition while one is running");
+                }
+                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
+            }
+        }
+
+        @Override
+        public void onTransitionEnd(Transition transition) {
+            mTransitionEnded = true;
+            hideWhenDone();
+            transition.removeListener(this);
+        }
+
+        @Override
+        public void hideSharedElements() {
+            mSharedElementHidden = true;
+            hideWhenDone();
+        }
+
+        private void hideWhenDone() {
+            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
+                mExit.resetViews();
+                int numSharedElements = mSharedElements.size();
+                for (int i = 0; i < numSharedElements; i++) {
+                    View view = mSharedElements.get(i);
+                    view.requestLayout();
+                }
+                View decorView = mWindow.getDecorView();
+                if (decorView != null) {
+                    decorView.setTagInternal(
+                            com.android.internal.R.id.cross_task_transition, null);
+                    decorView.setVisibility(View.GONE);
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7198146..a48065b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1166,12 +1166,25 @@
         @Override
         public void dumpDbInfo(final FileDescriptor fd, final String[] args) {
             if (mSystemThread) {
-                // Ensure this invocation is asynchronous to prevent
-                // writer waiting due to buffer cannot be consumed.
+                // Ensure this invocation is asynchronous to prevent writer waiting if buffer cannot
+                // be consumed. But it must duplicate the file descriptor first, since caller might
+                // be closing it.
+                final ParcelFileDescriptor dup;
+                try {
+                    dup = ParcelFileDescriptor.dup(fd);
+                } catch (IOException e) {
+                    Log.w(TAG, "Could not dup FD " + fd.getInt$());
+                    return;
+                }
+
                 AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
                     @Override
                     public void run() {
-                        dumpDatabaseInfo(fd, args);
+                        try {
+                            dumpDatabaseInfo(dup.getFileDescriptor(), args);
+                        } finally {
+                            IoUtils.closeQuietly(dup);
+                        }
                     }
                 });
             } else {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 02eb4d3..2219238 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -185,7 +185,12 @@
             activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
         }
         mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
-                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
+                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
+                mEnterActivityOptions.isCrossTask());
+        if (mEnterActivityOptions.isCrossTask()) {
+            mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+            mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
+        }
 
         if (!mIsEnterPostponed) {
             startEnter();
@@ -275,7 +280,8 @@
     }
 
     private void restoreReenteringViews() {
-        if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) {
+        if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() &&
+                !mEnterTransitionCoordinator.isCrossTask()) {
             mEnterTransitionCoordinator.forceViewsToAppear();
             mExitingFrom = null;
             mExitingTo = null;
@@ -302,8 +308,9 @@
                     }
                 }
 
-                mReturnExitCoordinator =
-                        new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
+                mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
+                        activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames,
+                        null, null, true);
                 if (enterViewsTransition != null && decor != null) {
                     enterViewsTransition.resume(decor);
                 }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 8bf1e9a..5d12b0d 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -59,12 +59,14 @@
     private boolean mIsViewsTransitionStarted;
     private Transition mEnterViewsTransition;
     private OnPreDrawListener mViewsReadyListener;
+    private final boolean mIsCrossTask;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
-            ArrayList<String> sharedElementNames, boolean isReturning) {
+            ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
         super(activity.getWindow(), sharedElementNames,
-                getListener(activity, isReturning), isReturning);
+                getListener(activity, isReturning && !isCrossTask), isReturning);
         mActivity = activity;
+        mIsCrossTask = isCrossTask;
         setResultReceiver(resultReceiver);
         prepareEnter();
         Bundle resultReceiverBundle = new Bundle();
@@ -85,6 +87,10 @@
         }
     }
 
+    boolean isCrossTask() {
+        return mIsCrossTask;
+    }
+
     public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,
             ArrayList<View> localViews) {
         boolean remap = false;
@@ -325,7 +331,9 @@
         if (mActivity == null || decorView == null) {
             return;
         }
-        mActivity.overridePendingTransition(0, 0);
+        if (!isCrossTask()) {
+            mActivity.overridePendingTransition(0, 0);
+        }
         if (!mIsReturning) {
             mWasOpaque = mActivity.convertToTranslucent(null, null);
             Drawable background = decorView.getBackground();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 0404288..160c285 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -35,6 +35,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.Window;
 
 import java.util.ArrayList;
 
@@ -59,18 +60,20 @@
     private Bundle mExitSharedElementBundle;
     private boolean mIsExitStarted;
     private boolean mSharedElementsHidden;
+    private HideSharedElementsCallback mHideSharedElementsCallback;
 
-    public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
+    public ExitTransitionCoordinator(Activity activity, Window window,
+            SharedElementCallback listener, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
-        super(activity.getWindow(), names, getListener(activity, isReturning), isReturning);
+        super(window, names, listener, isReturning);
         viewsReady(mapSharedElements(accepted, mapped));
         stripOffscreenViews();
         mIsBackgroundReady = !isReturning;
         mActivity = activity;
     }
 
-    private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
-        return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener;
+    void setHideSharedElementsCallback(HideSharedElementsCallback callback) {
+        mHideSharedElementsCallback = callback;
     }
 
     @Override
@@ -188,6 +191,9 @@
 
     private void hideSharedElements() {
         moveSharedElementsFromOverlay();
+        if (mHideSharedElementsCallback != null) {
+            mHideSharedElementsCallback.hideSharedElements();
+        }
         if (!mIsHidden) {
             hideViews(mSharedElements);
         }
@@ -207,7 +213,11 @@
             startTransition(new Runnable() {
                 @Override
                 public void run() {
-                    beginTransitions();
+                    if (mActivity != null) {
+                        beginTransitions();
+                    } else {
+                        startExitTransition();
+                    }
                 }
             });
         }
@@ -508,4 +518,8 @@
             return getWindow().getSharedElementExitTransition();
         }
     }
+
+    interface HideSharedElementsCallback {
+        void hideSharedElements();
+    }
 }
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 28e7887..da58717 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -22,8 +22,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -31,7 +31,9 @@
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -54,31 +56,37 @@
  * @see {@link ShortcutManager}.
  */
 public final class ShortcutInfo implements Parcelable {
-    /* @hide */
+    static final String TAG = "Shortcut";
+
+    private static final String RES_TYPE_STRING = "string";
+
+    private static final String ANDROID_PACKAGE_NAME = "android";
+
+    /** @hide */
     public static final int FLAG_DYNAMIC = 1 << 0;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_PINNED = 1 << 1;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_HAS_ICON_RES = 1 << 2;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_HAS_ICON_FILE = 1 << 3;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_MANIFEST = 1 << 5;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_DISABLED = 1 << 6;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
 
-    /* @hide */
+    /** @hide */
     public static final int FLAG_IMMUTABLE = 1 << 8;
 
     /** @hide */
@@ -99,20 +107,24 @@
 
     // Cloning options.
 
-    /* @hide */
+    /** @hide */
     private static final int CLONE_REMOVE_ICON = 1 << 0;
 
-    /* @hide */
+    /** @hide */
     private static final int CLONE_REMOVE_INTENT = 1 << 1;
 
-    /* @hide */
+    /** @hide */
     public static final int CLONE_REMOVE_NON_KEY_INFO = 1 << 2;
 
-    /* @hide */
-    public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON;
+    /** @hide */
+    public static final int CLONE_REMOVE_RES_NAMES = 1 << 3;
 
-    /* @hide */
-    public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_CREATOR = CLONE_REMOVE_ICON | CLONE_REMOVE_RES_NAMES;
+
+    /** @hide */
+    public static final int CLONE_REMOVE_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT
+            | CLONE_REMOVE_RES_NAMES;
 
     /** @hide */
     @IntDef(flag = true,
@@ -120,6 +132,7 @@
                     CLONE_REMOVE_ICON,
                     CLONE_REMOVE_INTENT,
                     CLONE_REMOVE_NON_KEY_INFO,
+                    CLONE_REMOVE_RES_NAMES,
                     CLONE_REMOVE_FOR_CREATOR,
                     CLONE_REMOVE_FOR_LAUNCHER
             })
@@ -144,16 +157,22 @@
 
     private int mTitleResId;
 
+    private String mTitleResName;
+
     @Nullable
     private CharSequence mTitle;
 
     private int mTextResId;
 
+    private String mTextResName;
+
     @Nullable
     private CharSequence mText;
 
     private int mDisabledMessageResId;
 
+    private String mDisabledMessageResName;
+
     @Nullable
     private CharSequence mDisabledMessage;
 
@@ -184,7 +203,9 @@
     private int mFlags;
 
     // Internal use only.
-    private int mIconResourceId;
+    private int mIconResId;
+
+    private String mIconResName;
 
     // Internal use only.
     @Nullable
@@ -251,7 +272,7 @@
         mLastChangedTimestamp = source.mLastChangedTimestamp;
 
         // Just always keep it since it's cheep.
-        mIconResourceId = source.mIconResourceId;
+        mIconResId = source.mIconResId;
 
         if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
             mActivity = source.mActivity;
@@ -274,37 +295,235 @@
             }
             mRank = source.mRank;
             mExtras = source.mExtras;
+
+            if ((cloneFlags & CLONE_REMOVE_RES_NAMES) == 0) {
+                mTitleResName = source.mTitleResName;
+                mTextResName = source.mTextResName;
+                mDisabledMessageResName = source.mDisabledMessageResName;
+                mIconResName = source.mIconResName;
+            }
         } else {
             // Set this bit.
             mFlags |= FLAG_KEY_FIELDS_ONLY;
         }
     }
 
-    /** @hide */
-    public void resolveStringsRequiringCrossUser(Context context) throws NameNotFoundException {
+    /**
+     * Load a string resource from the publisher app.
+     *
+     * @param resId resource ID
+     * @param defValue default value to be returned when the specified resource isn't found.
+     */
+    private CharSequence getResourceString(Resources res, int resId, CharSequence defValue) {
+        try {
+            return res.getString(resId);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource for ID=" + resId + " not found in package " + mPackageName);
+            return defValue;
+        }
+    }
+
+    /**
+     * Load the string resources for the text fields and set them to the actual value fields.
+     * This will set {@link #FLAG_STRINGS_RESOLVED}.
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    public void resolveResourceStrings(@NonNull Resources res) {
         mFlags |= FLAG_STRINGS_RESOLVED;
 
         if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)) {
             return; // Bail early.
         }
-        final Resources res = context.getPackageManager().getResourcesForApplicationAsUser(
-                mPackageName, mUserId);
 
         if (mTitleResId != 0) {
-            mTitle = res.getString(mTitleResId);
-            mTitleResId = 0;
+            mTitle = getResourceString(res, mTitleResId, mTitle);
         }
         if (mTextResId != 0) {
-            mText = res.getString(mTextResId);
-            mTextResId = 0;
+            mText = getResourceString(res, mTextResId, mText);
         }
         if (mDisabledMessageResId != 0) {
-            mDisabledMessage = res.getString(mDisabledMessageResId);
-            mDisabledMessageResId = 0;
+            mDisabledMessage = getResourceString(res, mDisabledMessageResId, mDisabledMessage);
         }
     }
 
     /**
+     * Look up resource name for a given resource ID.
+     *
+     * @return a simple resource name (e.g. "text_1") when {@code withType} is false, or with the
+     * type (e.g. "string/text_1").
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static String lookUpResourceName(@NonNull Resources res, int resId, boolean withType,
+            @NonNull String packageName) {
+        if (resId == 0) {
+            return null;
+        }
+        try {
+            final String fullName = res.getResourceName(resId);
+
+            if (ANDROID_PACKAGE_NAME.equals(getResourcePackageName(fullName))) {
+                // If it's a framework resource, the value won't change, so just return the ID
+                // value as a string.
+                return String.valueOf(resId);
+            }
+            return withType ? getResourceTypeAndEntryName(fullName)
+                    : getResourceEntryName(fullName);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
+                    + ". Resource IDs may change when the application is upgraded, and the system"
+                    + " may not be able to find the correct resource.");
+            return null;
+        }
+    }
+
+    /**
+     * Extract the package name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "com.android.app1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourcePackageName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(0, p1);
+    }
+
+    /**
+     * Extract the type name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "drawable"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceTypeName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        final int p2 = fullResourceName.indexOf('/', p1 + 1);
+        if (p2 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1, p2);
+    }
+
+    /**
+     * Extract the type name + the entry name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "drawable/icon1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceTypeAndEntryName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf(':');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1);
+    }
+
+    /**
+     * Extract the entry name from a fully-donated resource name.
+     * e.g. "com.android.app1:drawable/icon1" -> "icon1"
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getResourceEntryName(@NonNull String fullResourceName) {
+        final int p1 = fullResourceName.indexOf('/');
+        if (p1 < 0) {
+            return null;
+        }
+        return fullResourceName.substring(p1 + 1);
+    }
+
+    /**
+     * Return the resource ID for a given resource ID.
+     *
+     * Basically its' a wrapper over {@link Resources#getIdentifier(String, String, String)}, except
+     * if {@code resourceName} is an integer then it'll just return its value.  (Which also the
+     * aforementioned method would do internally, but not documented, so doing here explicitly.)
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static int lookUpResourceId(@NonNull Resources res, @Nullable String resourceName,
+            @Nullable String resourceType, String packageName) {
+        if (resourceName == null) {
+            return 0;
+        }
+        try {
+            try {
+                // It the name can be parsed as an integer, just use it.
+                return Integer.parseInt(resourceName);
+            } catch (NumberFormatException ignore) {
+            }
+
+            return res.getIdentifier(resourceName, resourceType, packageName);
+        } catch (NotFoundException e) {
+            Log.e(TAG, "Resource ID for name=" + resourceName + " not found in package "
+                    + packageName);
+            return 0;
+        }
+    }
+
+    /**
+     * Look up resource names from the resource IDs for the icon res and the text fields, and fill
+     * in the resource name fields.
+     *
+     * @param res {@link Resources} for the publisher.  Must have been loaded with
+     * {@link PackageManager#getResourcesForApplicationAsUser}.
+     *
+     * @hide
+     */
+    public void lookupAndFillInResourceNames(@NonNull Resources res) {
+        if ((mTitleResId == 0) && (mTextResId == 0) && (mDisabledMessageResId == 0)
+                && (mIconResId == 0)) {
+            return; // Bail early.
+        }
+
+        // We don't need types for strings because their types are always "string".
+        mTitleResName = lookUpResourceName(res, mTitleResId, /*withType=*/ false, mPackageName);
+        mTextResName = lookUpResourceName(res, mTextResId, /*withType=*/ false, mPackageName);
+        mDisabledMessageResName = lookUpResourceName(res, mDisabledMessageResId,
+                /*withType=*/ false, mPackageName);
+
+        // But icons have multiple possible types, so include the type.
+        mIconResName = lookUpResourceName(res, mIconResId, /*withType=*/ true, mPackageName);
+    }
+
+    /**
+     * Look up resource IDs from the resource names for the icon res and the text fields, and fill
+     * in the resource ID fields.
+     *
+     * This is called when an app is updated.
+     *
+     * @hide
+     */
+    public void lookupAndFillInResourceIds(@NonNull Resources res) {
+        if ((mTitleResName == null) && (mTextResName == null) && (mDisabledMessageResName == null)
+                && (mIconResName == null)) {
+            return; // Bail early.
+        }
+
+        mTitleResId = lookUpResourceId(res, mTitleResName, RES_TYPE_STRING, mPackageName);
+        mTextResId = lookUpResourceId(res, mTextResName, RES_TYPE_STRING, mPackageName);
+        mDisabledMessageResId = lookUpResourceId(res, mDisabledMessageResName, RES_TYPE_STRING,
+                mPackageName);
+
+        // mIconResName already contains the type, so the third argument is not needed.
+        mIconResId = lookUpResourceId(res, mIconResName, null, mPackageName);
+    }
+
+    /**
      * Copy a {@link ShortcutInfo}, optionally removing fields.
      * @hide
      */
@@ -344,27 +563,38 @@
 
         if (source.mIcon != null) {
             mIcon = source.mIcon;
+
+            mIconResId = 0;
+            mIconResName = null;
+            mBitmapPath = null;
         }
         if (source.mTitle != null) {
             mTitle = source.mTitle;
             mTitleResId = 0;
+            mTitleResName = null;
         } else if (source.mTitleResId != 0) {
             mTitle = null;
             mTitleResId = source.mTitleResId;
+            mTitleResName = null;
         }
+
         if (source.mText != null) {
             mText = source.mText;
             mTextResId = 0;
+            mTextResName = null;
         } else if (source.mTextResId != 0) {
             mText = null;
             mTextResId = source.mTextResId;
+            mTextResName = null;
         }
         if (source.mDisabledMessage != null) {
             mDisabledMessage = source.mDisabledMessage;
             mDisabledMessageResId = 0;
+            mDisabledMessageResName = null;
         } else if (source.mDisabledMessageResId != 0) {
             mDisabledMessage = null;
             mDisabledMessageResId = source.mDisabledMessageResId;
+            mDisabledMessageResName = null;
         }
         if (source.mCategories != null) {
             mCategories = clone(source.mCategories);
@@ -983,14 +1213,17 @@
 
     /** @hide */
     public void setIconResourceId(int iconResourceId) {
-        mIconResourceId = iconResourceId;
+        if (mIconResId != iconResourceId) {
+            mIconResName = null;
+        }
+        mIconResId = iconResourceId;
     }
 
     /**
      * Get the resource ID for the icon, valid only when {@link #hasIconResource()} } is true.
      */
     public int getIconResourceId() {
-        return mIconResourceId;
+        return mIconResId;
     }
 
     /** @hide */
@@ -1005,6 +1238,9 @@
 
     /** @hide */
     public void setDisabledMessageResId(int disabledMessageResId) {
+        if (mDisabledMessageResId != disabledMessageResId) {
+            mDisabledMessageResName = null;
+        }
         mDisabledMessageResId = disabledMessageResId;
         mDisabledMessage = null;
     }
@@ -1013,6 +1249,47 @@
     public void setDisabledMessage(String disabledMessage) {
         mDisabledMessage = disabledMessage;
         mDisabledMessageResId = 0;
+        mDisabledMessageResName = null;
+    }
+
+    /** @hide */
+    public String getTitleResName() {
+        return mTitleResName;
+    }
+
+    /** @hide */
+    public void setTitleResName(String titleResName) {
+        mTitleResName = titleResName;
+    }
+
+    /** @hide */
+    public String getTextResName() {
+        return mTextResName;
+    }
+
+    /** @hide */
+    public void setTextResName(String textResName) {
+        mTextResName = textResName;
+    }
+
+    /** @hide */
+    public String getDisabledMessageResName() {
+        return mDisabledMessageResName;
+    }
+
+    /** @hide */
+    public void setDisabledMessageResName(String disabledMessageResName) {
+        mDisabledMessageResName = disabledMessageResName;
+    }
+
+    /** @hide */
+    public String getIconResName() {
+        return mIconResName;
+    }
+
+    /** @hide */
+    public void setIconResName(String iconResName) {
+        mIconResName = iconResName;
     }
 
     private ShortcutInfo(Parcel source) {
@@ -1035,9 +1312,14 @@
         mExtras = source.readParcelable(cl);
         mLastChangedTimestamp = source.readLong();
         mFlags = source.readInt();
-        mIconResourceId = source.readInt();
+        mIconResId = source.readInt();
         mBitmapPath = source.readString();
 
+        mIconResName = source.readString();
+        mTitleResName = source.readString();
+        mTextResName = source.readString();
+        mDisabledMessageResName = source.readString();
+
         int N = source.readInt();
         if (N == 0) {
             mCategories = null;
@@ -1069,9 +1351,14 @@
         dest.writeParcelable(mExtras, flags);
         dest.writeLong(mLastChangedTimestamp);
         dest.writeInt(mFlags);
-        dest.writeInt(mIconResourceId);
+        dest.writeInt(mIconResId);
         dest.writeString(mBitmapPath);
 
+        dest.writeString(mIconResName);
+        dest.writeString(mTitleResName);
+        dest.writeString(mTextResName);
+        dest.writeString(mDisabledMessageResName);
+
         if (mCategories != null) {
             final int N = mCategories.size();
             dest.writeInt(N);
@@ -1160,16 +1447,25 @@
         sb.append(secure ? "***" : mTitle);
         sb.append(", resId=");
         sb.append(mTitleResId);
+        sb.append("[");
+        sb.append(mTitleResName);
+        sb.append("]");
 
         sb.append(", longLabel=");
         sb.append(secure ? "***" : mText);
         sb.append(", resId=");
         sb.append(mTextResId);
+        sb.append("[");
+        sb.append(mTextResName);
+        sb.append("]");
 
         sb.append(", disabledMessage=");
         sb.append(secure ? "***" : mDisabledMessage);
         sb.append(", resId=");
         sb.append(mDisabledMessageResId);
+        sb.append("[");
+        sb.append(mDisabledMessageResName);
+        sb.append("]");
 
         sb.append(", categories=");
         sb.append(mCategories);
@@ -1195,7 +1491,10 @@
         if (includeInternalData) {
 
             sb.append(", iconRes=");
-            sb.append(mIconResourceId);
+            sb.append(mIconResId);
+            sb.append("[");
+            sb.append(mIconResName);
+            sb.append("]");
 
             sb.append(", bitmapPath=");
             sb.append(mBitmapPath);
@@ -1208,11 +1507,13 @@
     /** @hide */
     public ShortcutInfo(
             @UserIdInt int userId, String id, String packageName, ComponentName activity,
-            Icon icon, CharSequence title, int titleResId, CharSequence text, int textResId,
-            CharSequence disabledMessage, int disabledMessageResId, Set<String> categories,
+            Icon icon, CharSequence title, int titleResId, String titleResName,
+            CharSequence text, int textResId, String textResName,
+            CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
+            Set<String> categories,
             Intent intent, PersistableBundle intentPersistableExtras,
             int rank, PersistableBundle extras, long lastChangedTimestamp,
-            int flags, int iconResId, String bitmapPath) {
+            int flags, int iconResId, String iconResName, String bitmapPath) {
         mUserId = userId;
         mId = id;
         mPackageName = packageName;
@@ -1220,10 +1521,13 @@
         mIcon = icon;
         mTitle = title;
         mTitleResId = titleResId;
+        mTitleResName = titleResName;
         mText = text;
         mTextResId = textResId;
+        mTextResName = textResName;
         mDisabledMessage = disabledMessage;
         mDisabledMessageResId = disabledMessageResId;
+        mDisabledMessageResName = disabledMessageResName;
         mCategories = clone(categories);
         mIntent = intent;
         mIntentPersistableExtras = intentPersistableExtras;
@@ -1231,7 +1535,8 @@
         mExtras = extras;
         mLastChangedTimestamp = lastChangedTimestamp;
         mFlags = flags;
-        mIconResourceId = iconResId;
+        mIconResId = iconResId;
+        mIconResName = iconResName;
         mBitmapPath = bitmapPath;
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 93fe73b..54a5968 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -126,9 +126,9 @@
      * Returns the most appropriate default theme for the specified target SDK version.
      * <ul>
      * <li>Below API 11: Gingerbread
-     * <li>APIs 11 thru 14: Holo
-     * <li>APIs 14 thru XX: Device default dark
-     * <li>API XX and above: Device default light with dark action bar
+     * <li>APIs 12 thru 14: Holo
+     * <li>APIs 15 thru 23: Device default dark
+     * <li>APIs 24 and above: Device default light with dark action bar
      * </ul>
      *
      * @param curTheme The current theme, or 0 if not specified.
@@ -156,7 +156,7 @@
         if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
             return holo;
         }
-        if (targetSdkVersion < Build.VERSION_CODES.CUR_DEVELOPMENT) {
+        if (targetSdkVersion < Build.VERSION_CODES.N) {
             return dark;
         }
         return deviceDefault;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index acb24e1..83a3bd8 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -691,12 +691,13 @@
         }
     }
 
-    // Read last_install; then report time for update and I/O to tron.
+    // Read last_install; then report time (in seconds) and I/O (in MiB) for
+    // this update to tron.
     // Only report on the reboots immediately after an OTA update.
     private static void parseLastInstallLog(Context context) {
         try (BufferedReader in = new BufferedReader(new FileReader(LAST_INSTALL_FILE))) {
             String line = null;
-            int bytesWritten = -1, bytesStashed = -1;
+            int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;
             int timeTotal = -1;
             while ((line = in.readLine()) != null) {
                 // Here is an example of lines in last_install:
@@ -709,20 +710,35 @@
                     continue;
                 }
                 String numString = line.substring(numIndex + 1).trim();
-                int parsedNum;
+                long parsedNum;
                 try {
-                    parsedNum = Integer.parseInt(numString);
+                    parsedNum = Long.parseLong(numString);
                 } catch (NumberFormatException ignored) {
                     Log.e(TAG, "Failed to parse numbers in " + line);
                     continue;
                 }
 
+                final int MiB = 1024 * 1024;
+                int scaled;
+                try {
+                    if (line.startsWith("bytes")) {
+                        scaled = Math.toIntExact(parsedNum / MiB);
+                    } else {
+                        scaled = Math.toIntExact(parsedNum);
+                    }
+                } catch (ArithmeticException ignored) {
+                    Log.e(TAG, "Number overflows in " + line);
+                    continue;
+                }
+
                 if (line.startsWith("time")) {
-                    timeTotal = parsedNum;
+                    timeTotal = scaled;
                 } else if (line.startsWith("bytes_written")) {
-                    bytesWritten = (bytesWritten == -1) ? parsedNum : bytesWritten + parsedNum;
+                    bytesWrittenInMiB = (bytesWrittenInMiB == -1) ? scaled :
+                            bytesWrittenInMiB + scaled;
                 } else if (line.startsWith("bytes_stashed")) {
-                    bytesStashed = (bytesStashed == -1) ? parsedNum : bytesStashed + parsedNum;
+                    bytesStashedInMiB = (bytesStashedInMiB == -1) ? scaled :
+                            bytesStashedInMiB + scaled;
                 }
             }
 
@@ -730,15 +746,15 @@
             if (timeTotal != -1) {
                 MetricsLogger.histogram(context, "ota_time_total", timeTotal);
             }
-            if (bytesWritten != -1) {
-                MetricsLogger.histogram(context, "ota_bytes_written", bytesWritten);
+            if (bytesWrittenInMiB != -1) {
+                MetricsLogger.histogram(context, "ota_written_in_MiBs", bytesWrittenInMiB);
             }
-            if (bytesStashed != -1) {
-                MetricsLogger.histogram(context, "ota_bytes_stashed", bytesStashed);
+            if (bytesStashedInMiB != -1) {
+                MetricsLogger.histogram(context, "ota_stashed_in_MiBs", bytesStashedInMiB);
             }
 
-        } catch (IOException ignored) {
-            Log.e(TAG, "Failed to read lines in last_install", ignored);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to read lines in last_install", e);
         }
     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 4b0cb09..9b0ef8e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2045,7 +2045,7 @@
     }
 
     /**
-     * Returns a {@code Bundle} containing any saved application restrictions for this user, for the
+     * Returns a {@link Bundle} containing any saved application restrictions for this user, for the
      * given package name. Only an application with this package name can call this method.
      *
      * <p>The returned {@link Bundle} consists of key-value pairs, as defined by the application,
@@ -2058,8 +2058,8 @@
      * </ul>
      *
      * @param packageName the package name of the calling application
-     * @return a {@code Bundle} with the restrictions for that package, or {@code null} if there
-     * are no saved restrictions.
+     * @return a {@link Bundle} with the restrictions for that package, or an empty {@link Bundle}
+     * if there are no saved restrictions.
      *
      * @see #KEY_RESTRICTIONS_PENDING
      */
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 485bbd1..968996f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -45,8 +45,11 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -115,6 +118,10 @@
     public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
 
     private static volatile IMountService sMountService = null;
+    private static final String INTERNAL_STORAGE_SIZE_PATH =
+            "/sys/block/mmcblk0/size";
+    private static final String INTERNAL_STORAGE_SECTOR_SIZE =
+            "/sys/block/mmcblk0/queue/hw_sector_size";
 
     private final Context mContext;
     private final ContentResolver mResolver;
@@ -903,6 +910,23 @@
         return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
     }
 
+    /** {@hide} */
+    public long getPrimaryStorageSize() {
+        final long numberBlocks = readLong(INTERNAL_STORAGE_SIZE_PATH);
+        final long sectorSize = readLong(INTERNAL_STORAGE_SECTOR_SIZE);
+        return numberBlocks * sectorSize;
+    }
+
+    private long readLong(String path) {
+        try (final FileInputStream fis = new FileInputStream(path);
+                final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) {
+            return Long.parseLong(reader.readLine());
+        } catch (Exception e) {
+            Slog.w("Could not read " + path, e);
+            return 0;
+        }
+    }
+
     /** @removed */
     public @NonNull StorageVolume[] getVolumeList() {
         return getVolumeList(mContext.getUserId(), 0);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 787202e..dc8e4b9 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -19,7 +19,6 @@
 import android.annotation.Nullable;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.text.style.ParagraphStyle;
 import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
@@ -554,7 +553,8 @@
         if (adjustSelection) {
             boolean changed = false;
             if (selectionStart > start && selectionStart < end) {
-                final int offset = (selectionStart - start) * newLen / origLen;
+                final long diff = selectionStart - start;
+                final int offset = Math.toIntExact(diff * newLen / origLen);
                 selectionStart = start + offset;
 
                 changed = true;
@@ -562,7 +562,8 @@
                         Spanned.SPAN_POINT_POINT);
             }
             if (selectionEnd > start && selectionEnd < end) {
-                final int offset = (selectionEnd - start) * newLen / origLen;
+                final long diff = selectionEnd - start;
+                final int offset = Math.toIntExact(diff * newLen / origLen);
                 selectionEnd = start + offset;
 
                 changed = true;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 316c7e3..8823605 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1941,6 +1941,26 @@
     }
 
     /**
+     * Force the transition to move to its end state, ending all the animators.
+     *
+     * @hide
+     */
+    void forceToEnd(ViewGroup sceneRoot) {
+        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+        int numOldAnims = runningAnimators.size();
+        if (sceneRoot != null) {
+            WindowId windowId = sceneRoot.getWindowId();
+            for (int i = numOldAnims - 1; i >= 0; i--) {
+                AnimationInfo info = runningAnimators.valueAt(i);
+                if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
+                    Animator anim = runningAnimators.keyAt(i);
+                    anim.end();
+                }
+            }
+        }
+    }
+
+    /**
      * This method cancels a transition that is currently running.
      *
      * @hide
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 71c8099..f2c871e3 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -440,7 +440,7 @@
             ArrayList<Transition> copy = new ArrayList(runningTransitions);
             for (int i = copy.size() - 1; i >= 0; i--) {
                 final Transition transition = copy.get(i);
-                transition.end();
+                transition.forceToEnd(sceneRoot);
             }
         }
 
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 583dc0f..a41fe64 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -16,8 +16,6 @@
 
 package android.transition;
 
-import com.android.internal.R;
-
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -26,6 +24,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 
 /**
@@ -498,6 +498,16 @@
         }
     }
 
+    /** @hide */
+    @Override
+    void forceToEnd(ViewGroup sceneRoot) {
+        super.forceToEnd(sceneRoot);
+        int numTransitions = mTransitions.size();
+        for (int i = 0; i < numTransitions; ++i) {
+            mTransitions.get(i).forceToEnd(sceneRoot);
+        }
+    }
+
     @Override
     TransitionSet setSceneRoot(ViewGroup sceneRoot) {
         super.setSceneRoot(sceneRoot);
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 52f35de..d0d4507 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -16,7 +16,6 @@
 
 package android.view.accessibility;
 
-import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -102,9 +101,8 @@
     /**
      * Gets the title of the window.
      *
-     * @return The title of the window, or {@code null} if none is available.
+     * @return The title.
      */
-    @Nullable
     public CharSequence getTitle() {
         return mTitle;
     }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9ad750d..18408aa 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -528,22 +528,16 @@
 
     @Override
     public void setTitle(CharSequence title) {
-        setTitle(title, true);
-    }
-
-    public void setTitle(CharSequence title, boolean updateAccessibilityTitle) {
         if (mTitleView != null) {
             mTitleView.setText(title);
         } else if (mDecorContentParent != null) {
             mDecorContentParent.setWindowTitle(title);
         }
         mTitle = title;
-        if (updateAccessibilityTitle) {
-            WindowManager.LayoutParams params = getAttributes();
-            if (!TextUtils.equals(title, params.accessibilityTitle)) {
-                params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
-                dispatchWindowAttributesChanged(getAttributes());
-            }
+        WindowManager.LayoutParams params = getAttributes();
+        if (!TextUtils.equals(title, params.accessibilityTitle)) {
+            params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
+            dispatchWindowAttributesChanged(getAttributes());
         }
     }
 
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
index e46e6b0..dc3832a 100644
--- a/core/java/com/android/internal/util/FastPrintWriter.java
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -345,7 +345,7 @@
             if (mOutputStream != null) {
                 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
                 CoderResult result = mCharset.encode(charBuffer, mBytes, true);
-                while (true) {
+                while (!mIoError) {
                     if (result.isError()) {
                         throw new IOException(result.toString());
                     } else if (result.isOverflow()) {
diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
index 2653745..7d222c7 100644
--- a/core/java/com/android/internal/util/WakeupMessage.java
+++ b/core/java/com/android/internal/util/WakeupMessage.java
@@ -45,24 +45,32 @@
     protected final String mCmdName;
     @VisibleForTesting
     protected final int mCmd, mArg1, mArg2;
+    @VisibleForTesting
+    protected final Object mObj;
     private boolean mScheduled;
 
     public WakeupMessage(Context context, Handler handler,
-            String cmdName, int cmd, int arg1, int arg2) {
+            String cmdName, int cmd, int arg1, int arg2, Object obj) {
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mHandler = handler;
         mCmdName = cmdName;
         mCmd = cmd;
         mArg1 = arg1;
         mArg2 = arg2;
+        mObj = obj;
     }
 
     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
-        this(context, handler, cmdName, cmd, arg1, 0);
+        this(context, handler, cmdName, cmd, arg1, 0, null);
+    }
+
+    public WakeupMessage(Context context, Handler handler,
+            String cmdName, int cmd, int arg1, int arg2) {
+        this(context, handler, cmdName, cmd, arg1, arg2, null);
     }
 
     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
-        this(context, handler, cmdName, cmd, 0, 0);
+        this(context, handler, cmdName, cmd, 0, 0, null);
     }
 
     /**
@@ -99,7 +107,7 @@
             mScheduled = false;
         }
         if (stillScheduled) {
-            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+            Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
             mHandler.handleMessage(msg);
             msg.recycle();
         }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 1beea8c..2e0dfa5 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -916,23 +916,6 @@
     }
 
     /**
-     * Gets whether the device is encrypted.
-     *
-     * @return Whether the device is encrypted.
-     */
-    public static boolean isDeviceEncrypted() {
-        IMountService mountService = IMountService.Stub.asInterface(
-                ServiceManager.getService("mount"));
-        try {
-            return mountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE
-                    && mountService.getPasswordType() != StorageManager.CRYPT_TYPE_DEFAULT;
-        } catch (RemoteException re) {
-            Log.e(TAG, "Error getting encryption state", re);
-        }
-        return true;
-    }
-
-    /**
      * Determine if the device supports encryption, even if it's set to default. This
      * differs from isDeviceEncrypted() in that it returns true even if the device is
      * encrypted with the default password.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b2028a7..89abeff 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1418,6 +1418,11 @@
     <permission android:name="android.permission.DVB_DEVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows reading the OEM unlock state
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_OEM_UNLOCK_STATE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @hide Allows enabling/disabling OEM unlock
    <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.OEM_UNLOCK_STATE"
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 7f8acd3..5c165e6 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -122,9 +122,11 @@
 
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_PROGRESS}. -->
   <item type="id" name="accessibilityActionSetProgress" />
-  
+
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->
   <item type="id" name="accessibilityActionContextClick" />
 
   <item type="id" name="remote_input_tag" />
+
+  <item type="id" name="cross_task_transition" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8edd9d1..1009294 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2612,4 +2612,7 @@
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
 
   <java-symbol type="string" name="suspended_widget_accessibility" />
+
+  <!-- Used internally for assistant to launch activity transitions -->
+  <java-symbol type="id" name="cross_task_transition" />
 </resources>
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index 0815172..a7ea43e 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -35,6 +35,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
 
 public class BluetoothTestUtils extends Assert {
 
@@ -505,40 +507,47 @@
      * @param adapter The BT adapter.
      */
     public void discoverable(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-
         if (!adapter.isEnabled()) {
             fail("discoverable() bluetooth not enabled");
         }
 
         int scanMode = adapter.getScanMode();
-        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
             return;
         }
 
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-
-        while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
-            scanMode = adapter.getScanMode();
-            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("discoverable() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
+        final Semaphore completionSemaphore = new Semaphore(0);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
+                    return;
+                }
+                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+                        BluetoothAdapter.SCAN_MODE_NONE);
+                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+                    completionSemaphore.release();
+                }
             }
-            sleep(POLL_TIME);
-        }
+        };
 
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
-                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
-                firedFlags, mask));
+        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+        boolean success = false;
+        try {
+            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
+                    TimeUnit.MILLISECONDS);
+            writeOutput(String.format("discoverable() completed in 0 ms"));
+        } catch (final InterruptedException e) {
+            // This should never happen but just in case it does, the test will fail anyway.
+        }
+        mContext.unregisterReceiver(receiver);
+        if (!success) {
+            fail(String.format("discoverable() timeout: scanMode=%d (expected %d)", scanMode,
+                    BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+        }
     }
 
     /**
@@ -548,40 +557,47 @@
      * @param adapter The BT adapter.
      */
     public void undiscoverable(BluetoothAdapter adapter) {
-        int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
-
         if (!adapter.isEnabled()) {
             fail("undiscoverable() bluetooth not enabled");
         }
 
         int scanMode = adapter.getScanMode();
-        if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+        if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
             return;
         }
 
-        BluetoothReceiver receiver = getBluetoothReceiver(mask);
-
-        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
-        long start = System.currentTimeMillis();
-        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-
-        while (System.currentTimeMillis() - start < DISCOVERABLE_UNDISCOVERABLE_TIMEOUT) {
-            scanMode = adapter.getScanMode();
-            if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE
-                    && (receiver.getFiredFlags() & mask) == mask) {
-                writeOutput(String.format("undiscoverable() completed in %d ms",
-                        (receiver.getCompletedTime() - start)));
-                removeReceiver(receiver);
-                return;
+        final Semaphore completionSemaphore = new Semaphore(0);
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                if (!BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
+                    return;
+                }
+                final int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+                        BluetoothAdapter.SCAN_MODE_NONE);
+                if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+                    completionSemaphore.release();
+                }
             }
-            sleep(POLL_TIME);
-        }
+        };
 
-        int firedFlags = receiver.getFiredFlags();
-        removeReceiver(receiver);
-        fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
-                + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
-                mask));
+        final IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+        boolean success = false;
+        try {
+            success = completionSemaphore.tryAcquire(DISCOVERABLE_UNDISCOVERABLE_TIMEOUT,
+                    TimeUnit.MILLISECONDS);
+            writeOutput(String.format("undiscoverable() completed in 0 ms"));
+        } catch (InterruptedException e) {
+            // This should never happen but just in case it does, the test will fail anyway.
+        }
+        mContext.unregisterReceiver(receiver);
+        if (!success) {
+            fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d)", scanMode,
+                    BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+        }
     }
 
     /**
diff --git a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
index da8bc1d..7935880 100644
--- a/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java
@@ -45,6 +45,7 @@
     private static final int TEST_CMD = 18;
     private static final int TEST_ARG1 = 33;
     private static final int TEST_ARG2 = 182;
+    private static final Object TEST_OBJ = "hello";
 
     @Mock AlarmManager mAlarmManager;
     WakeupMessage mMessage;
@@ -92,7 +93,7 @@
                 mListenerCaptor.capture(), any(Handler.class));
 
         mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1,
-                TEST_ARG2);
+                TEST_ARG2, TEST_OBJ);
     }
 
     /**
@@ -114,6 +115,7 @@
         assertEquals("what", TEST_CMD, mHandler.getLastMessage().what);
         assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1);
         assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2);
+        assertEquals("obj", TEST_OBJ, mHandler.getLastMessage().obj);
     }
 
     /**
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 3bf6d25..f287f51 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -841,6 +841,8 @@
   to: /studio/run/index.html#instant-run
 - from: /r/studio-ui/test-recorder.html
   to: http://tools.android.com/tech-docs/test-recorder
+- from: /r/studio-ui/export-licenses.html
+  to: http://tools.android.com/tech-docs/new-build-system/license
 - from: /reference/org/apache/http/...
   to: /about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
 - from: /shareables/...
diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd
index 6384773..424983d 100644
--- a/docs/html/distribute/index.jd
+++ b/docs/html/distribute/index.jd
@@ -67,7 +67,9 @@
 
         </li>
         <li class="dac-tab-view" data-tab-view="type">
+          <!--
           <div class="dac-filter-item" data-filter-value="type:distribute" data-filter-name="Articles"></div>
+          -->
           <div class="dac-filter-item" data-filter-value="type:youtube" data-filter-name="Videos"></div>
           <div class="dac-filter-item" data-filter-value="type:blog" data-filter-name="Blog posts"></div>
         </li>
@@ -78,7 +80,7 @@
   </div>
 
   <div class="resource-flow-layout col-16" id="latest-resources"
-      data-query="type:distribute, type:youtube+tag:googleplay, type:blog+tag:googleplay+tag:featured"
+      data-query="type:youtube+tag:googleplay, type:blog+tag:googleplay+tag:featured"
       data-sortOrder="-timestamp"
       data-cardSizes="6x6"
       data-items-per-page="6"
diff --git a/docs/html/preview/features/data-saver.jd b/docs/html/preview/features/data-saver.jd
index 761556e..abc47ea 100644
--- a/docs/html/preview/features/data-saver.jd
+++ b/docs/html/preview/features/data-saver.jd
@@ -179,10 +179,13 @@
   Testing with Android Debug Bridge Commands
 </h2>
 
-The <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge (ADB)</a>
-provides a few commands that you can use to check and configure network
-permissions:
-
+<p>
+  The <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge (ADB)</a>
+  provides a few commands that you can use to test your app in Data Saver
+  conditions. You can check and configure network
+  permissions or set wireless networks as metered to test your app on unmetered
+  networks.
+</p>
 <dl>
   <dt>
     <code>$ adb shell dumpsys netpolicy</code>
@@ -231,4 +234,23 @@
     Removes the specified package UID from the whitelist to block background
     metered data usage while Data Saver is enabled.
   </dd>
+
+  <dt>
+    <code>$ adb shell cmd netpolicy list wifi-networks</code>
+  </dt>
+
+  <dd>
+    Lists all wifi networks, displaying whether they're metered.
+  </dd>
+
+
+  <dt>
+    <code>$  adb shell cmd netpolicy set metered-network &lt;WIFI_SSID&gt;
+      true</code>
+  </dt>
+
+  <dd>
+    Sets wifi with the specified SSID as metered, allowing you to simulate a
+    metered network on an unmetered network.
+  </dd>
 </dl>
\ No newline at end of file
diff --git a/docs/html/preview/features/picture-in-picture.jd b/docs/html/preview/features/picture-in-picture.jd
index 893f8c1..c089feb 100644
--- a/docs/html/preview/features/picture-in-picture.jd
+++ b/docs/html/preview/features/picture-in-picture.jd
@@ -150,11 +150,20 @@
 Picture-in-picture</h2>
 
 <p>When your activity switches to PIP, the system considers the activity in a
-paused state, and calls your activity's <code>onPause()</code> method. Video
-playback should not be paused and should continue playing if the activity is
-paused due to PIP mode. Check for PIP in your activity's
-<code>onPause()</code> method and handle playback appropriately, for
-example:</p>
+paused state, and calls your activity's {@link android.app.Activity#onPause
+onPause()} method. Video playback should not be paused and should continue
+playing if the activity is paused due to PIP mode.</p>
+
+<p>In Android N, you should pause and resume video playback when the system
+calls your activity's {@link android.app.Activity#onStop onStop()} and
+{@link android.app.Activity#onStart onStart()}. By doing this, you can avoid
+having to check if your app is in PIP mode in
+{@link android.app.Activity#onPause onPause()} and explicitly
+continuing playback.</p>
+
+<p>If you have to pause playback in your {@link android.app.Activity#onPause
+onPause()} implementation, check for PIP mode by calling {@code
+isInPictureInPictureMode()} and handle playback appropriately, for example:</p>
 
 <pre>
 &#64;Override
@@ -170,7 +179,8 @@
 </pre>
 
 <p>When your activity switches out of PIP mode back to full-screen mode, the
-system resumes your activity and calls your <code>onResume()</code> method.</p>
+system resumes your activity and calls your
+{@link android.app.Activity#onResume onResume()} method.</p>
 
 <h2 id="single_playback">Using a Single Playback Activity for
 Picture-in-picture</h2>
@@ -211,3 +221,11 @@
 <p>When an activity is in PIP mode, by default it doesn't get input focus. To
 receive input events while in PIP mode, use
 <code>MediaSession.setMediaButtonReceiver()</code>.</p>
+
+<p>When your app is in PIP mode, video playback in the PIP window can cause
+audio interference with another app, such as a music player app or voice search
+app. To avoid this, request audio focus when you start playing the video,
+and handle audio focus change notifications, as described in
+<a href="{@docRoot}training/managing-audio/audio-focus.html">Managing Audio
+Focus</a>. If you receive notification of audio focus loss when in PIP mode,
+pause or stop video playback.</p>
\ No newline at end of file
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index b0631ad..918de48 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -57,7 +57,7 @@
 
 <div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand">
   <div class="wrap dac-offset-parent">
-    <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps">
+    <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest">
       <i class="dac-sprite dac-arrow-down-gray"></i>
     </a>
     <ul class="dac-actions">
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 6d6a4b5..7392453 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -46,6 +46,8 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.File;
 import java.text.Collator;
 import java.text.Normalizer;
@@ -123,8 +125,8 @@
         mPm = mContext.getPackageManager();
         mIpm = AppGlobals.getPackageManager();
         mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
-        for (UserHandle user : mUm.getUserProfiles()) {
-            mEntriesMap.put(user.getIdentifier(), new HashMap<String, AppEntry>());
+        for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
+            mEntriesMap.put(userId, new HashMap<String, AppEntry>());
         }
         mThread = new HandlerThread("ApplicationsState.Loader",
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -426,7 +428,8 @@
     }
 
     private void addUser(int userId) {
-        if (mUm.getUserProfiles().contains(new UserHandle(userId))) {
+        final int profileIds[] = mUm.getProfileIdsWithDisabled(UserHandle.myUserId());
+        if (ArrayUtils.contains(profileIds, userId)) {
             synchronized (mEntriesMap) {
                 mEntriesMap.put(userId, new HashMap<String, AppEntry>());
                 if (mResumed) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 8df7ac5..08364d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -20,14 +20,17 @@
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.TypedArray;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.support.v4.widget.DrawerLayout;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 import android.view.Gravity;
@@ -58,6 +61,9 @@
 
     private static List<DashboardCategory> sDashboardCategories;
     private static HashMap<Pair<String, String>, Tile> sTileCache;
+    // Serves as a temporary list of tiles to ignore until we heard back from the PM that they
+    // are disabled.
+    private static ArraySet<ComponentName> sTileBlacklist = new ArraySet<>();
     private static InterestingConfigChanges sConfigTracker;
 
     private final PackageReceiver mPackageReceiver = new PackageReceiver();
@@ -288,6 +294,24 @@
         finish();
     }
 
+    public void setTileEnabled(ComponentName component, boolean enabled) {
+        PackageManager pm = getPackageManager();
+        int state = pm.getComponentEnabledSetting(component);
+        boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        if (isEnabled != enabled || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+            if (enabled) {
+                sTileBlacklist.remove(component);
+            } else {
+                sTileBlacklist.add(component);
+            }
+            pm.setComponentEnabledSetting(component, enabled
+                    ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                    : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+            new CategoriesUpdater().execute();
+        }
+    }
+
     public interface CategoryListener {
         void onCategoriesChanged();
     }
@@ -303,6 +327,15 @@
 
         @Override
         protected void onPostExecute(List<DashboardCategory> dashboardCategories) {
+            for (int i = 0; i < dashboardCategories.size(); i++) {
+                DashboardCategory category = dashboardCategories.get(i);
+                for (int j = 0; j < category.tiles.size(); j++) {
+                    Tile tile = category.tiles.get(j);
+                    if (sTileBlacklist.contains(tile.intent.getComponent())) {
+                        category.tiles.remove(j--);
+                    }
+                }
+            }
             sDashboardCategories = dashboardCategories;
             onCategoriesChanged();
         }
diff --git a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
new file mode 100644
index 0000000..ba5c0aa
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/date_time_alarm_group"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:layout_marginStart="16dp"
+    android:gravity="start"
+    android:orientation="vertical">
+    <LinearLayout
+        android:id="@+id/date_time_group"
+        android:layout_width="wrap_content"
+        android:layout_height="19dp"
+        android:orientation="horizontal"
+        android:focusable="true" >
+
+        <include layout="@layout/split_clock_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:id="@+id/clock" />
+
+        <com.android.systemui.statusbar.policy.DateView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6dp"
+            android:drawableStart="@drawable/header_dot"
+            android:drawablePadding="6dp"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+            android:textSize="@dimen/qs_time_collapsed_size"
+            android:gravity="top"
+            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/alarm_status_collapsed"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_access_alarms_small"
+            android:paddingStart="6dp"
+            android:gravity="center"
+            android:visibility="gone" />
+    </LinearLayout>
+
+    <com.android.systemui.statusbar.AlphaOptimizedButton
+        android:id="@+id/alarm_status"
+        android:layout_width="wrap_content"
+        android:layout_height="20dp"
+        android:paddingTop="3dp"
+        android:drawablePadding="8dp"
+        android:drawableStart="@drawable/ic_access_alarms_small"
+        android:textColor="#64ffffff"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+        android:gravity="top"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 678ec4e..103e0b0 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -118,64 +118,12 @@
         android:gravity="center_vertical"
         android:focusable="true" />
 
-    <LinearLayout
+    <include
         android:id="@+id/date_time_alarm_group"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
+        layout="@layout/status_bar_alarm_group"
         android:layout_marginTop="16dp"
-        android:layout_marginStart="16dp"
-        android:gravity="start"
-        android:orientation="vertical">
-        <LinearLayout
-            android:id="@+id/date_time_group"
-            android:layout_width="wrap_content"
-            android:layout_height="19dp"
-            android:orientation="horizontal"
-            android:focusable="true" >
-
-            <include layout="@layout/split_clock_view"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:id="@+id/clock" />
-
-            <com.android.systemui.statusbar.policy.DateView
-                android:id="@+id/date"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginStart="6dp"
-                android:drawableStart="@drawable/header_dot"
-                android:drawablePadding="6dp"
-                android:singleLine="true"
-                android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-                android:textSize="@dimen/qs_time_collapsed_size"
-                android:gravity="top"
-                systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
-
-            <com.android.systemui.statusbar.AlphaOptimizedImageView
-                android:id="@+id/alarm_status_collapsed"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:src="@drawable/ic_access_alarms_small"
-                android:paddingStart="6dp"
-                android:gravity="center"
-                android:visibility="gone" />
-        </LinearLayout>
-
-        <com.android.systemui.statusbar.AlphaOptimizedButton
-            android:id="@+id/alarm_status"
-            android:layout_width="wrap_content"
-            android:layout_height="20dp"
-            android:paddingTop="3dp"
-            android:drawablePadding="8dp"
-            android:drawableStart="@drawable/ic_access_alarms_small"
-            android:textColor="#64ffffff"
-            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:gravity="top"
-            android:background="?android:attr/selectableItemBackground"
-            android:visibility="gone" />
-    </LinearLayout>
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true" />
 
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
@@ -189,7 +137,7 @@
         android:clipToPadding="false"
         android:importantForAccessibility="yes"
         android:focusable="true"
-        android:accessibilityTraversalAfter="@id/date_time_group"
+        android:accessibilityTraversalAfter="@+id/date_time_group"
         android:accessibilityTraversalBefore="@id/expand_indicator" />
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 2db97a6..0c8cc9b 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -43,7 +43,7 @@
             android:ellipsize="start"
             android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
             android:textIsSelectable="true"
-            android:imeOptions="actionSend|flagNoExtractUi" />
+            android:imeOptions="actionNone|flagNoExtractUi" />
 
     <FrameLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
new file mode 100644
index 0000000..f94b727
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/date_time_alarm_group"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:layout_marginStart="16dp"
+    android:gravity="start"
+    android:orientation="vertical">
+    <LinearLayout
+        android:id="@+id/date_time_group"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:focusable="true" >
+
+        <include layout="@layout/split_clock_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:id="@+id/clock" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/alarm_status_collapsed"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_access_alarms_small"
+            android:paddingStart="6dp"
+            android:gravity="center"
+            android:visibility="gone" />
+    </LinearLayout>
+
+    <com.android.systemui.statusbar.policy.DateView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:layout_marginTop="-4dp"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textSize="@dimen/qs_time_collapsed_size"
+        android:gravity="top"
+        systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+    <com.android.systemui.statusbar.AlphaOptimizedButton
+        android:id="@+id/alarm_status"
+        android:layout_width="wrap_content"
+        android:layout_height="20dp"
+        android:paddingTop="3dp"
+        android:drawablePadding="8dp"
+        android:drawableStart="@drawable/ic_access_alarms_small"
+        android:textColor="#64ffffff"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+        android:gravity="top"
+        android:background="?android:attr/selectableItemBackground"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-sw410dp/config.xml b/packages/SystemUI/res/values-sw410dp/config.xml
index 049a535..b04b28c 100644
--- a/packages/SystemUI/res/values-sw410dp/config.xml
+++ b/packages/SystemUI/res/values-sw410dp/config.xml
@@ -22,5 +22,5 @@
 <resources>
     <integer name="quick_settings_num_rows">2</integer>
 
-    <bool name="quick_settings_show_date">true</bool>
+    <bool name="quick_settings_show_full_alarm">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e98309e..134388f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,9 +103,19 @@
         wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location
     </string>
 
+    <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
+    <string name="quick_settings_tiles_stock" translatable="false">
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,work,cast,night
+    </string>
+
     <!-- The tiles to display in QuickSettings -->
     <string name="quick_settings_tiles" translatable="false">default</string>
 
+    <!-- The tiles to display in QuickSettings in retail mode -->
+    <string name="quick_settings_tiles_retail_mode" translatable="false">
+        cell,battery,dnd,flashlight,rotation,location
+    </string>
+
     <!-- Whether or not the RSSI tile is capitalized or not. -->
     <bool name="quick_settings_rssi_tile_capitalization">true</bool>
 
@@ -262,7 +272,6 @@
     <!-- Nav bar button default ordering/layout -->
     <string name="config_navBarLayout" translatable="false">space,back;home;recent,menu_ime</string>
 
-    <bool name="quick_settings_show_date">false</bool>
+    <bool name="quick_settings_show_full_alarm">false</bool>
 
 </resources>
-
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index f92c51f..2dcb5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -20,6 +20,7 @@
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnLayoutChangeListener;
 import android.widget.TextView;
+
 import com.android.systemui.qs.PagedTileLayout.PageListener;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.QSTile.Host.Callback;
@@ -65,6 +66,7 @@
     private boolean mFullRows;
     private int mNumQuickTiles;
     private float mLastPosition;
+    private QSTileHost mHost;
 
     public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
         mQsContainer = container;
@@ -94,6 +96,7 @@
     }
 
     public void setHost(QSTileHost qsh) {
+        mHost = qsh;
         qsh.addCallback(this);
         updateAnimators();
     }
@@ -106,6 +109,9 @@
 
     @Override
     public void onViewDetachedFromWindow(View v) {
+        if (mHost != null) {
+            mHost.removeCallback(this);
+        }
         TunerService.get(mQsContainer.getContext()).removeTunable(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 4bf85c7..8a0079d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -141,6 +142,7 @@
             mNotifQsContainer.setCustomizerShowing(true);
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_desc_quick_settings_edit));
+            mHost.getKeyguardMonitor().addCallback(mKeyguardCallback);
         }
     }
 
@@ -156,6 +158,7 @@
             mNotifQsContainer.setCustomizerShowing(false);
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_desc_quick_settings));
+            mHost.getKeyguardMonitor().removeCallback(mKeyguardCallback);
         }
     }
 
@@ -201,6 +204,15 @@
         mTileAdapter.saveSpecs(mHost);
     }
 
+    private final Callback mKeyguardCallback = new Callback() {
+        @Override
+        public void onKeyguardChanged() {
+            if (mHost.getKeyguardMonitor().isShowing()) {
+                hide(0, 0);
+            }
+        }
+    };
+
     private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 8ec6a2f..b38dd17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -55,8 +55,7 @@
     }
 
     private void addSystemTiles(final QSTileHost host) {
-        String possible = mContext.getString(R.string.quick_settings_tiles_default)
-                + ",hotspot,inversion,saver,work,cast,night";
+        String possible = mContext.getString(R.string.quick_settings_tiles_stock);
         String[] possibleTiles = possible.split(",");
         final Handler qsHandler = new Handler(host.getLooper());
         final Handler mainHandler = new Handler(Looper.getMainLooper());
@@ -141,9 +140,16 @@
             PackageManager pm = mContext.getPackageManager();
             List<ResolveInfo> services = pm.queryIntentServicesAsUser(
                     new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser());
+            String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
             for (ResolveInfo info : services) {
                 String packageName = info.serviceInfo.packageName;
                 ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);
+
+                // Don't include apps that are a part of the default tile set.
+                if (stockTiles.contains(componentName.flattenToString())) {
+                    continue;
+                }
+
                 final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);
                 String spec = CustomTile.toSpec(componentName);
                 State state = getState(params[0], spec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 16b1158..87d6307 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -134,9 +134,14 @@
             }
             if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser);
             mBindTryCount++;
-            mIsBound = mContext.bindServiceAsUser(mIntent, this,
-                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                    mUser);
+            try {
+                mIsBound = mContext.bindServiceAsUser(mIntent, this,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                        mUser);
+            } catch (SecurityException e) {
+                Log.e(TAG, "Failed to bind to service", e);
+                mIsBound = false;
+            }
         } else {
             if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser);
             // Give it another chance next time it needs to be bound, out of kindness.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 82a5622..ce9bbf4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -21,13 +21,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.TileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
+
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+
+import java.util.List;
+
 import libcore.util.Objects;
 
 /**
@@ -222,15 +229,29 @@
             if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                 return;
             }
-            if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                return;
-            }
+
             Uri data = intent.getData();
             String pkgName = data.getEncodedSchemeSpecificPart();
             final ComponentName component = mStateManager.getComponent();
             if (!Objects.equal(pkgName, component.getPackageName())) {
                 return;
             }
+
+            // If the package is being updated, verify the component still exists.
+            if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                Intent queryIntent = new Intent(TileService.ACTION_QS_TILE);
+                queryIntent.setPackage(pkgName);
+                PackageManager pm = context.getPackageManager();
+                List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+                        queryIntent, 0, ActivityManager.getCurrentUser());
+                for (ResolveInfo info : services) {
+                    if (Objects.equal(info.serviceInfo.packageName, component.getPackageName())
+                            && Objects.equal(info.serviceInfo.name, component.getClassName())) {
+                        return;
+                    }
+                }
+            }
+
             mServices.getHost().removeTile(component);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 68e5d0b..ae1e5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -27,6 +27,7 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
@@ -320,6 +321,9 @@
             return;
         }
         if (DEBUG) Log.d(TAG, "Recreating tiles");
+        if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
+            newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
+        }
         final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
         int currentUser = ActivityManager.getCurrentUser();
         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 3688495..95b8344 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.RippleDrawable;
+import android.os.UserManager;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -91,6 +92,7 @@
     private float mExpansionAmount;
     private QSTileHost mHost;
     private View mEdit;
+    private boolean mShowFullAlarm;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -111,8 +113,7 @@
         mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
         mDateTimeGroup.setPivotX(0);
         mDateTimeGroup.setPivotY(0);
-        boolean showDate = getResources().getBoolean(R.bool.quick_settings_show_date);
-        findViewById(R.id.date).setVisibility(showDate ? View.VISIBLE : View.GONE);
+        mShowFullAlarm = getResources().getBoolean(R.bool.quick_settings_show_full_alarm);
 
         mExpandIndicator = (ExpandableIndicator) findViewById(R.id.expand_indicator);
 
@@ -162,14 +163,16 @@
         mDateScaleFactor = dateExpandedSize / dateCollapsedSize;
 
         mSecondHalfAnimator = new TouchAnimator.Builder()
-                .addFloat(mAlarmStatus, "alpha", 0, 1)
+                .addFloat(mShowFullAlarm ? mAlarmStatus : findViewById(R.id.date), "alpha", 0, 1)
                 .addFloat(mEmergencyOnly, "alpha", 0, 1)
                 .setStartDelay(.5f)
                 .build();
-        mFirstHalfAnimator = new TouchAnimator.Builder()
-                .addFloat(mAlarmStatusCollapsed, "alpha", 1, 0)
-                .setEndDelay(.5f)
-                .build();
+        if (mShowFullAlarm) {
+            mFirstHalfAnimator = new TouchAnimator.Builder()
+                    .addFloat(mAlarmStatusCollapsed, "alpha", 1, 0)
+                    .setEndDelay(.5f)
+                    .build();
+        }
         mDateSizeAnimator = new TouchAnimator.Builder()
                 .addFloat(mDateTimeGroup, "scaleX", 1, mDateScaleFactor)
                 .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
@@ -240,7 +243,9 @@
     public void setExpansion(float headerExpansionFraction) {
         mExpansionAmount = headerExpansionFraction;
         mSecondHalfAnimator.setPosition(headerExpansionFraction);
-        mFirstHalfAnimator.setPosition(headerExpansionFraction);
+        if (mShowFullAlarm) {
+            mFirstHalfAnimator.setPosition(headerExpansionFraction);
+        }
         mDateSizeAnimator.setPosition(headerExpansionFraction);
         mSettingsAlpha.setPosition(headerExpansionFraction);
 
@@ -258,7 +263,7 @@
     }
 
     private void updateAlarmVisibilities() {
-        mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
+        mAlarmStatus.setVisibility(mAlarmShowing && mShowFullAlarm ? View.VISIBLE : View.INVISIBLE);
         mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
     }
 
@@ -283,8 +288,10 @@
                 ? View.VISIBLE : View.INVISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
-        mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers()
+        final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
+        mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
                 ? View.VISIBLE : View.INVISIBLE);
+        mEdit.setVisibility(isDemo ? View.INVISIBLE : View.VISIBLE);
     }
 
     private void updateListeners() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index d5f1707..7c391fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -873,7 +873,8 @@
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             ExpandableNotificationRow parent = row.getNotificationParent();
             if (parent != null && parent.areChildrenExpanded()
-                    && (mGearExposedView == parent
+                    && (parent.areGutsExposed()
+                        || mGearExposedView == parent
                         || (parent.getNotificationChildren().size() == 1
                                 && parent.isClearable()))) {
                 // In this case the group is expanded and showing the gear for the
@@ -961,6 +962,7 @@
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) v).isExpandable()
+                && !((ExpandableNotificationRow) v).areGutsExposed()
                 && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
     }
 
@@ -2097,7 +2099,9 @@
         final ExpandableView firstChild = getFirstChildNotGone();
         int firstChildMinHeight = firstChild != null
                 ? firstChild.getIntrinsicHeight()
-                : mCollapsedSize;
+                : mEmptyShadeView != null
+                        ? mEmptyShadeView.getMinHeight()
+                        : mCollapsedSize;
         if (mOwnScrollY > 0) {
             firstChildMinHeight = Math.max(firstChildMinHeight - mOwnScrollY, mCollapsedSize);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 5d26988..c8c7d3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -171,6 +171,12 @@
     }
 
     public static boolean canChildBeDismissed(View v) {
+        if (v instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+            if (row.areGutsExposed()) {
+                return false;
+            }
+        }
         final View veto = v.findViewById(R.id.veto);
         return (veto != null && veto.getVisibility() != View.GONE);
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 578bc8d..1adefc4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -797,6 +797,17 @@
         return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
     }
 
+    // Used only for testing.
+    // TODO: Delete this and either:
+    // 1. Give Fake SettingsProvider the ability to send settings change notifications (requires
+    //    changing ContentResolver to make registerContentObserver non-final).
+    // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
+    //    by subclassing SettingsObserver.
+    @VisibleForTesting
+    void updateMobileDataAlwaysOn() {
+        mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+    }
+
     private void handleMobileDataAlwaysOn() {
         final boolean enable = (Settings.Global.getInt(
                 mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1);
@@ -3391,7 +3402,6 @@
      *         was no always-on VPN to start. {@code false} otherwise.
      */
     private boolean startAlwaysOnVpn(int userId) {
-        final String alwaysOnPackage;
         synchronized (mVpns) {
             Vpn vpn = mVpns.get(userId);
             if (vpn == null) {
@@ -3400,27 +3410,8 @@
                 Slog.wtf(TAG, "User " + userId + " has no Vpn configuration");
                 return false;
             }
-            alwaysOnPackage = vpn.getAlwaysOnPackage();
-            // Skip if there is no service to start.
-            if (alwaysOnPackage == null) {
-                return true;
-            }
-            // Skip if the service is already established. This isn't bulletproof: it's not bound
-            // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
-            // which may restart the connection.
-            if (vpn.getNetworkInfo().isConnected()) {
-                return true;
-            }
-        }
 
-        // Start the VPN service declared in the app's manifest.
-        Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
-        serviceIntent.setPackage(alwaysOnPackage);
-        try {
-            return mContext.startServiceAsUser(serviceIntent, UserHandle.of(userId)) != null;
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "VpnService " + serviceIntent + " failed to start", e);
-            return false;
+            return vpn.startAlwaysOnVpn();
         }
     }
 
@@ -3452,17 +3443,7 @@
                 return false;
             }
 
-            // Save the configuration
-            final long token = Binder.clearCallingIdentity();
-            try {
-                final ContentResolver cr = mContext.getContentResolver();
-                Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
-                        packageName, userId);
-                Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
-                        (lockdown ? 1 : 0), userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            vpn.saveAlwaysOnPackage();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 3745e0b..83d6344 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -17,10 +17,12 @@
 package com.android.server;
 
 import android.Manifest.permission;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.net.INetworkScoreCache;
@@ -67,6 +69,20 @@
     private NetworkScorerPackageMonitor mPackageMonitor;
     private ScoringServiceConnection mServiceConnection;
 
+    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (DBG) Log.d(TAG, "Received " + action + " for userId " + userId);
+            if (userId == UserHandle.USER_NULL) return;
+
+            if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                onUserUnlocked(userId);
+            }
+        }
+    };
+
     /**
      * Clears scores when the active scorer package is no longer valid and
      * manages the service connection.
@@ -138,6 +154,11 @@
     public NetworkScoreService(Context context) {
         mContext = context;
         mScoreCaches = new HashMap<>();
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        // TODO: Need to update when we support per-user scorers. http://b/23422763
+        mContext.registerReceiverAsUser(
+                mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
+                null /* scheduler */);
     }
 
     /** Called when the system is ready to run third-party code but before it actually does so. */
@@ -164,6 +185,11 @@
         bindToScoringServiceIfNeeded();
     }
 
+    private void onUserUnlocked(int userId) {
+        registerPackageMonitorIfNeeded();
+        bindToScoringServiceIfNeeded();
+    }
+
     private void registerPackageMonitorIfNeeded() {
         if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
         NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
@@ -216,6 +242,8 @@
 
             // Make sure the connection is connected (idempotent)
             mServiceConnection.connect(mContext);
+        } else { // otherwise make sure it isn't bound.
+            unbindFromScoringServiceIfNeeded();
         }
     }
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 51037dd..1e0b693 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -126,10 +126,20 @@
         SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
     }
 
-    private void enforceOemUnlockPermission() {
+    private void enforceOemUnlockReadPermission() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
+                == PackageManager.PERMISSION_DENIED
+                && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
+                == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException("Can't access OEM unlock state. Requires "
+                    + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
+        }
+    }
+
+    private void enforceOemUnlockWritePermission() {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.OEM_UNLOCK_STATE,
-                "Can't access OEM unlock state");
+                "Can't modify OEM unlock state");
     }
 
     private void enforceUid(int callingUid) {
@@ -435,7 +445,7 @@
 
         @Override
         public void wipe() {
-            enforceOemUnlockPermission();
+            enforceOemUnlockWritePermission();
 
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
@@ -453,7 +463,7 @@
                 return;
             }
 
-            enforceOemUnlockPermission();
+            enforceOemUnlockWritePermission();
             enforceIsAdmin();
 
             // Do not allow oem unlock modification if it has been disallowed.
@@ -472,13 +482,13 @@
 
         @Override
         public boolean getOemUnlockEnabled() {
-            enforceOemUnlockPermission();
+            enforceOemUnlockReadPermission();
             return doGetOemUnlockEnabled();
         }
 
         @Override
         public int getFlashLockState() {
-            enforceOemUnlockPermission();
+            enforceOemUnlockReadPermission();
             String locked = SystemProperties.get(FLASH_LOCK_PROP);
             switch (locked) {
                 case FLASH_LOCK_LOCKED:
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4439a24..d83a750 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3764,7 +3764,8 @@
         r.finishLaunchTickingLocked();
     }
 
-    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
+    private void removeActivityFromHistoryLocked(
+            ActivityRecord r, TaskRecord oldTop, String reason) {
         mStackSupervisor.removeChildActivityContainers(r);
         finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
         r.makeFinishingLocked();
@@ -3783,10 +3784,11 @@
             validateAppTokensLocked();
         }
         final TaskRecord task = r.task;
+        final TaskRecord topTask = oldTop != null ? oldTop : topTask();
         if (task != null && task.removeActivity(r)) {
             if (DEBUG_STACK) Slog.i(TAG_STACK,
                     "removeActivityFromHistoryLocked: last activity removed from " + this);
-            if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
+            if (mStackSupervisor.isFocusedStack(this) && task == topTask &&
                     task.isOverHomeStack()) {
                 mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
             }
@@ -3922,6 +3924,12 @@
 
         boolean removedFromHistory = false;
 
+        // If the activity is finishing, it's no longer considered in topRunningActivityLocked,
+        // and cleanUpActivityLocked() may change focus to another activity (or task).
+        // Get the current top task now, as removeActivityFromHistoryLocked() below need this
+        // to decide whether to return to home stack after removal.
+        final TaskRecord topTask = topTask();
+
         cleanUpActivityLocked(r, false, false);
 
         final boolean hadApp = r.app != null;
@@ -3956,7 +3964,8 @@
                 // up.
                 //Slog.w(TAG, "Exception thrown during finish", e);
                 if (r.finishing) {
-                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
+                    removeActivityFromHistoryLocked(
+                            r, topTask, reason + " exceptionInScheduleDestroy");
                     removedFromHistory = true;
                     skipDestroy = true;
                 }
@@ -3987,7 +3996,7 @@
         } else {
             // remove this record from the history.
             if (r.finishing) {
-                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
+                removeActivityFromHistoryLocked(r, topTask, reason + " hadNoApp");
                 removedFromHistory = true;
             } else {
                 if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
@@ -4018,7 +4027,7 @@
             if (isInStackLocked(r) != null) {
                 if (r.state == ActivityState.DESTROYING) {
                     cleanUpActivityLocked(r, true, false);
-                    removeActivityFromHistoryLocked(r, reason);
+                    removeActivityFromHistoryLocked(r, null, reason);
                 }
             }
             mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -4176,7 +4185,7 @@
                     }
                     cleanUpActivityLocked(r, true, true);
                     if (remove) {
-                        removeActivityFromHistoryLocked(r, "appDied");
+                        removeActivityFromHistoryLocked(r, null, "appDied");
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1cf121c..1d1dc2b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -870,6 +870,8 @@
                 }
             }
         }
+        // Send launch end powerhint when idle
+        mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
         return true;
     }
 
@@ -2755,6 +2757,9 @@
             }
         }
 
+        // Send launch end powerhint before going sleep
+        mService.mActivityStarter.sendPowerHintForLaunchEndIfNeeded();
+
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -4422,7 +4427,7 @@
         // Work Challenge is present) let startActivityInPackage handle the intercepting.
         if (!mService.mUserController.shouldConfirmCredentials(task.userId)
                 && task.getRootActivity() != null) {
-            mService.mActivityStarter.sendPowerHintForLaunchIfNeeded(true /* forceSend */);
+            mService.mActivityStarter.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */);
             mActivityMetricsLogger.notifyActivityLaunching();
             mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
             mActivityMetricsLogger.notifyActivityLaunched(ActivityManager.START_TASK_TO_FRONT,
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b022c4f..b89cae7 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -212,8 +212,6 @@
         mKeepCurTransition = false;
         mAvoidMoveToFront = false;
 
-        mPowerHintSent = false;
-
         mVoiceSession = null;
         mVoiceInteractor = null;
     }
@@ -947,8 +945,8 @@
         return START_SUCCESS;
     }
 
-    void sendPowerHintForLaunchIfNeeded(boolean forceSend) {
-        // Trigger launch power hint if activity is not in the current task
+    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend) {
+        // Trigger launch power hint if activity being launched is not in the current task
         final ActivityStack focusStack = mSupervisor.getFocusedStack();
         final ActivityRecord curTop = (focusStack == null)
             ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
@@ -956,11 +954,19 @@
                 curTop.task != null && mStartActivity != null &&
                 curTop.task != mStartActivity.task )) &&
                 mService.mLocalPowerManager != null) {
-            mService.mLocalPowerManager.powerHint(PowerManagerInternal.POWER_HINT_LAUNCH, 0);
+            mService.mLocalPowerManager.powerHint(PowerManagerInternal.POWER_HINT_LAUNCH, 1);
             mPowerHintSent = true;
         }
     }
 
+    void sendPowerHintForLaunchEndIfNeeded() {
+        // Trigger launch power hint if activity is launched
+        if (mPowerHintSent && mService.mLocalPowerManager != null) {
+            mService.mLocalPowerManager.powerHint(PowerManagerInternal.POWER_HINT_LAUNCH, 0);
+            mPowerHintSent = false;
+        }
+    }
+
     private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
@@ -1022,7 +1028,7 @@
                 }
             }
 
-            sendPowerHintForLaunchIfNeeded(false /* forceSend */);
+            sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
 
             mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);
 
@@ -1147,7 +1153,7 @@
                 EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
         mTargetStack.mLastPausedActivity = null;
 
-        sendPowerHintForLaunchIfNeeded(false /* forceSend */);
+        sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
 
         mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
         if (mDoResume) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 8932e4b..c84aaac 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1537,6 +1537,11 @@
                 ? Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
 
+        // Always set fontScale to be euqal to global. Can't set to 0, as that makes the override
+        // config not equal to EMPTY. Also can't set to 1, as Configuration.updateFrom will use
+        // the override scale as long as it's non-zero, and we'll always use 1.
+        config.fontScale = serviceConfig.fontScale;
+
         // For calculating screen layout, we need to use the non-decor inset screen area for the
         // calculation for compatibility reasons, i.e. screen area without system bars that could
         // never go away in Honeycomb.
@@ -1564,6 +1569,7 @@
         extracted.smallestScreenWidthDp = config.smallestScreenWidthDp;
         extracted.orientation = config.orientation;
         extracted.screenLayout = config.screenLayout;
+        extracted.fontScale = config.fontScale;
         return extracted;
     }
 
@@ -1597,6 +1603,9 @@
         newScreenLayout = (newScreenLayout & ~SCREENLAYOUT_SIZE_MASK)
                 | (overrideScreenLayout & SCREENLAYOUT_SIZE_MASK);
         mOverrideConfig.screenLayout = newScreenLayout;
+        // we never override the fontScale, however we need to copy over the global value
+        // so that the default 1.0 doesn't get applied as an override.
+        mOverrideConfig.fontScale = globalConfig.fontScale;
     }
 
     static Rect validateBounds(Rect bounds) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1bdb48a..ebacc71 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -30,6 +30,7 @@
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -53,6 +54,7 @@
 import android.net.NetworkMisc;
 import android.net.RouteInfo;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.FileUtils;
 import android.os.IBinder;
@@ -60,12 +62,14 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.PatternMatcher;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.text.TextUtils;
@@ -163,6 +167,45 @@
     // Handle of user initiating VPN.
     private final int mUserHandle;
 
+    // Listen to package remove and change event in this user
+    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final Uri data = intent.getData();
+            final String packageName = data == null ? null : data.getSchemeSpecificPart();
+            if (packageName == null) {
+                return;
+            }
+
+            synchronized (Vpn.this) {
+                // Avoid race that always-on package has been unset
+                if (!packageName.equals(getAlwaysOnPackage())) {
+                    return;
+                }
+
+                final String action = intent.getAction();
+                Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
+                        + " in user " + mUserHandle);
+
+                switch(action) {
+                    case Intent.ACTION_PACKAGE_REPLACED:
+                        // Start vpn after app upgrade
+                        startAlwaysOnVpn();
+                        break;
+                    case Intent.ACTION_PACKAGE_REMOVED:
+                        final boolean isPackageRemoved = !intent.getBooleanExtra(
+                                Intent.EXTRA_REPLACING, false);
+                        if (isPackageRemoved) {
+                            setAndSaveAlwaysOnPackage(null, false);
+                        }
+                        break;
+                }
+            }
+        }
+    };
+
+    private boolean mIsPackageIntentReceiverRegistered = false;
+
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
             int userHandle) {
         mContext = context;
@@ -233,10 +276,37 @@
 
         mAlwaysOn = (packageName != null);
         mLockdown = (mAlwaysOn && lockdown);
+        maybeRegisterPackageChangeReceiverLocked(packageName);
         setVpnForcedLocked(mLockdown);
         return true;
     }
 
+    private void unregisterPackageChangeReceiverLocked() {
+        // register previous intent filter
+        if (mIsPackageIntentReceiverRegistered) {
+            mContext.unregisterReceiver(mPackageIntentReceiver);
+            mIsPackageIntentReceiverRegistered = false;
+        }
+    }
+
+    private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
+        // Unregister IntentFilter listening for previous always-on package change
+        unregisterPackageChangeReceiverLocked();
+
+        if (packageName != null) {
+            mIsPackageIntentReceiverRegistered = true;
+
+            IntentFilter intentFilter = new IntentFilter();
+            // Protected intent can only be sent by system. No permission required in register.
+            intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            intentFilter.addDataScheme("package");
+            intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
+            mContext.registerReceiverAsUser(
+                    mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
+        }
+    }
+
     /**
      * @return the package name of the VPN controller responsible for always-on VPN,
      *         or {@code null} if none is set or always-on VPN is controlled through
@@ -249,6 +319,69 @@
     }
 
     /**
+     * Save the always-on package and lockdown config into Settings.Secure
+     */
+    public synchronized void saveAlwaysOnPackage() {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final ContentResolver cr = mContext.getContentResolver();
+            Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
+                    getAlwaysOnPackage(), mUserHandle);
+            Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
+                    (mLockdown ? 1 : 0), mUserHandle);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Set and save always-on package and lockdown config
+     * @see Vpn#setAlwaysOnPackage(String, boolean)
+     * @see Vpn#saveAlwaysOnPackage()
+     *
+     * @return result of Vpn#setAndSaveAlwaysOnPackage(String, boolean)
+     */
+    private synchronized boolean setAndSaveAlwaysOnPackage(String packageName, boolean lockdown) {
+        if (setAlwaysOnPackage(packageName, lockdown)) {
+            saveAlwaysOnPackage();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @return {@code true} if the service was started, the service was already connected, or there
+     *         was no always-on VPN to start. {@code false} otherwise.
+     */
+    public boolean startAlwaysOnVpn() {
+        final String alwaysOnPackage;
+        synchronized (this) {
+            alwaysOnPackage = getAlwaysOnPackage();
+            // Skip if there is no service to start.
+            if (alwaysOnPackage == null) {
+                return true;
+            }
+            // Skip if the service is already established. This isn't bulletproof: it's not bound
+            // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
+            // which may restart the connection.
+            if (getNetworkInfo().isConnected()) {
+                return true;
+            }
+        }
+
+        // Start the VPN service declared in the app's manifest.
+        Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+        serviceIntent.setPackage(alwaysOnPackage);
+        try {
+            return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
+        } catch (RuntimeException e) {
+            Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
+            return false;
+        }
+    }
+
+    /**
      * Prepare for a VPN application. This method is designed to solve
      * race conditions. It first compares the current prepared package
      * with {@code oldPackage}. If they are the same, the prepared
@@ -270,11 +403,12 @@
      *
      * - oldPackage non-null, newPackage null: App calling VpnService#prepare().
      * - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
-     * - oldPackage non-null, newPackage=LEGACY_VPN: Used internally to disconnect
+     * - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
      *   and revoke any current app VPN and re-prepare legacy vpn.
      *
-     * TODO: Rename the variables - or split this method into two - and end this
-     * confusion.
+     * TODO: Rename the variables - or split this method into two - and end this confusion.
+     * TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
+     * to prepare(oldPackage=null, newPackage=LEGACY_VPN)
      *
      * @param oldPackage The package name of the old VPN application
      * @param newPackage The package name of the new VPN application
@@ -284,10 +418,7 @@
     public synchronized boolean prepare(String oldPackage, String newPackage) {
         if (oldPackage != null) {
             // Stop an existing always-on VPN from being dethroned by other apps.
-            // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
-            // can unset always-on after always-on package is uninstalled. Make sure when package
-            // is reinstalled, the consent dialog is not shown.
-            if (mAlwaysOn && !TextUtils.equals(mPackage, oldPackage)) {
+            if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
                 return false;
             }
 
@@ -318,9 +449,7 @@
         enforceControlPermission();
 
         // Stop an existing always-on VPN from being dethroned by other apps.
-        // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
-        // can unset always-on after always-on package is uninstalled
-        if (mAlwaysOn && !TextUtils.equals(mPackage, newPackage)) {
+        if (mAlwaysOn && !isCurrentPreparedPackage(newPackage)) {
             return false;
         }
 
@@ -862,6 +991,7 @@
         setVpnForcedLocked(false);
         mAlwaysOn = false;
 
+        unregisterPackageChangeReceiverLocked();
         // Quit any active connections
         agentDisconnect();
     }
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/services/core/java/com/android/server/notification/ScheduleCalendar.java
index 9267d82..22ca702 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/services/core/java/com/android/server/notification/ScheduleCalendar.java
@@ -58,11 +58,7 @@
         final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute);
         long nextScheduleTime = Math.min(nextStart, nextEnd);
 
-        if (mSchedule.exitAtAlarm && mSchedule.nextAlarm > now) {
-            return Math.min(nextScheduleTime, mSchedule.nextAlarm);
-        } else {
-            return nextScheduleTime;
-        }
+        return nextScheduleTime;
     }
 
     private long getNextTime(long now, int hr, int min) {
@@ -124,4 +120,4 @@
         mCalendar.add(Calendar.DATE, days);
         return mCalendar.getTimeInMillis();
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index e3dcf14..8197544 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -43,8 +43,8 @@
  * Built-in zen condition provider for daily scheduled time-based conditions.
  */
 public class ScheduleConditionProvider extends SystemConditionProviderService {
-    private static final String TAG = "ConditionProviders.SCP";
-    private static final boolean DEBUG = true || Log.isLoggable("ConditionProviders", Log.DEBUG);
+    static final String TAG = "ConditionProviders.SCP";
+    static final boolean DEBUG = true || Log.isLoggable("ConditionProviders", Log.DEBUG);
 
     public static final ComponentName COMPONENT =
             new ComponentName("android", ScheduleConditionProvider.class.getName());
@@ -154,6 +154,9 @@
                 cal.maybeSetNextAlarm(now, nextUserAlarmTime);
             } else {
                 notifyCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
+                if (nextUserAlarmTime == 0) {
+                    cal.maybeSetNextAlarm(now, nextUserAlarmTime);
+                }
             }
             if (cal != null) {
                 final long nextChangeTime = cal.getNextChangeTime(now);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 05f5b8f..07dc404 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1448,6 +1448,9 @@
         pw.println("      -s: short summary");
         pw.println("      -d: only list dangerous permissions");
         pw.println("      -u: list only the permissions users will see");
+        pw.println("  dump-profiles TARGET-PACKAGE");
+        pw.println("    Dumps method/class profile files to");
+        pw.println("    /data/misc/profman/TARGET-PACKAGE.txt");
         pw.println("  resolve-activity [--brief] [--components] [--user USER_ID] INTENT");
         pw.println("    Prints the activity that resolves to the given Intent.");
         pw.println("  query-activities [--brief] [--components] [--user USER_ID] INTENT");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index e0c28fa..f336ff3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -19,9 +19,12 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
 import android.os.PersistableBundle;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
@@ -48,6 +51,8 @@
 import java.util.Set;
 import java.util.function.Predicate;
 
+import sun.misc.Resource;
+
 /**
  * Package information used by {@link ShortcutService}.
  * User information used by {@link ShortcutService}.
@@ -72,15 +77,19 @@
     private static final String ATTR_ACTIVITY = "activity";
     private static final String ATTR_TITLE = "title";
     private static final String ATTR_TITLE_RES_ID = "titleid";
+    private static final String ATTR_TITLE_RES_NAME = "titlename";
     private static final String ATTR_TEXT = "text";
     private static final String ATTR_TEXT_RES_ID = "textid";
+    private static final String ATTR_TEXT_RES_NAME = "textname";
     private static final String ATTR_DISABLED_MESSAGE = "dmessage";
     private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
+    private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
     private static final String ATTR_INTENT = "intent";
     private static final String ATTR_RANK = "rank";
     private static final String ATTR_TIMESTAMP = "timestamp";
     private static final String ATTR_FLAGS = "flags";
-    private static final String ATTR_ICON_RES = "icon-res";
+    private static final String ATTR_ICON_RES_ID = "icon-res";
+    private static final String ATTR_ICON_RES_NAME = "icon-resname";
     private static final String ATTR_BITMAP_PATH = "bitmap-path";
 
     private static final String NAME_CATEGORIES = "categories";
@@ -153,6 +162,12 @@
         }
     }
 
+    @Nullable
+    public Resources getPackageResources() {
+        return mShortcutUser.mService.injectGetResourcesForApplicationAsUser(
+                getPackageName(), getPackageUserId());
+    }
+
     @Override
     protected void onRestoreBlocked() {
         // Can't restore due to version/signature mismatch.  Remove all shortcuts.
@@ -209,8 +224,13 @@
     }
 
     private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
+        final ShortcutService s = mShortcutUser.mService;
+
         deleteShortcutInner(newShortcut.getId());
-        mShortcutUser.mService.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
+
+        // Extract Icon and update the icon res ID and the bitmap path.
+        s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
+        s.fixUpShortcutResourceNamesAndValues(newShortcut);
         mShortcuts.put(newShortcut.getId(), newShortcut);
     }
 
@@ -248,7 +268,7 @@
         // TODO Check max dynamic count.
         // mShortcutUser.mService.enforceMaxDynamicShortcuts(newDynamicCount);
 
-        // Okay, make it dynamic and add.
+        // If it was originally pinned, the new one should be pinned too.
         if (wasPinned) {
             newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
         }
@@ -318,6 +338,8 @@
                 disabled.setDisabledMessage(disabledMessage);
             } else if (disabledMessageResId != 0) {
                 disabled.setDisabledMessageResId(disabledMessageResId);
+
+                mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
             }
         }
     }
@@ -622,10 +644,25 @@
 
         // For existing shortcuts, update timestamps if they have any resources.
         if (!isNewApp) {
+            Resources publisherRes = null;
+
             for (int i = mShortcuts.size() - 1; i >= 0; i--) {
                 final ShortcutInfo si = mShortcuts.valueAt(i);
 
                 if (si.hasAnyResources()) {
+                    if (!si.isOriginallyFromManifest()) {
+                        if (publisherRes == null) {
+                            publisherRes = getPackageResources();
+                            if (publisherRes == null) {
+                                break; // Resources couldn't be loaded.
+                            }
+                        }
+
+                        // If this shortcut is not from a manifest, then update all resource IDs
+                        // from resource names.  (We don't allow resource strings for
+                        // non-manifest at the moment, but icons can still be resources.)
+                        si.lookupAndFillInResourceIds(publisherRes);
+                    }
                     changed = true;
                     si.setTimestamp(s.injectCurrentTimeMillis());
                 }
@@ -869,7 +906,9 @@
             final ComponentName newActivity = newShortcut.getActivity();
             if (newActivity == null) {
                 if (operation != ShortcutService.OPERATION_UPDATE) {
-                    service.wtf("null Activity found for non-update");
+                    // This method may be called before validating shortcuts, so this may happen,
+                    // and is a caller side error.
+                    throw new NullPointerException("activity must be provided");
                 }
                 continue; // Activity can be null for update.
             }
@@ -907,6 +946,36 @@
         }
     }
 
+    /**
+     * For all the text fields, refresh the string values if they're from resources.
+     */
+    public void resolveResourceStrings() {
+        final ShortcutService s = mShortcutUser.mService;
+        boolean changed = false;
+
+        Resources publisherRes = null;
+        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+            final ShortcutInfo si = mShortcuts.valueAt(i);
+
+            if (si.hasStringResources()) {
+                changed = true;
+
+                if (publisherRes == null) {
+                    publisherRes = getPackageResources();
+                    if (publisherRes == null) {
+                        break; // Resources couldn't be loaded.
+                    }
+                }
+
+                si.resolveResourceStrings(publisherRes);
+                si.setTimestamp(s.injectCurrentTimeMillis());
+            }
+        }
+        if (changed) {
+            s.scheduleSaveUser(getPackageUserId());
+        }
+    }
+
     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         pw.println();
 
@@ -1008,11 +1077,15 @@
         // writeAttr(out, "icon", si.getIcon());  // We don't save it.
         ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
         ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
+        ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
         ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
         ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
+        ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
         ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE, si.getDisabledMessage());
         ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_ID,
                 si.getDisabledMessageResourceId());
+        ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
+                si.getDisabledMessageResName());
         ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
         ShortcutService.writeAttr(out, ATTR_RANK, si.getRank());
         ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
@@ -1025,7 +1098,8 @@
                             | ShortcutInfo.FLAG_DYNAMIC));
         } else {
             ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
-            ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
+            ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId());
+            ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName());
             ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
         }
 
@@ -1096,17 +1170,21 @@
         // Icon icon;
         String title;
         int titleResId;
+        String titleResName;
         String text;
         int textResId;
+        String textResName;
         String disabledMessage;
         int disabledMessageResId;
+        String disabledMessageResName;
         Intent intent;
         PersistableBundle intentPersistableExtras = null;
         int rank;
         PersistableBundle extras = null;
         long lastChangedTimestamp;
         int flags;
-        int iconRes;
+        int iconResId;
+        String iconResName;
         String bitmapPath;
         ArraySet<String> categories = null;
 
@@ -1115,16 +1193,21 @@
                 ATTR_ACTIVITY);
         title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
         titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
+        titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
         text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
         textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
+        textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
         disabledMessage = ShortcutService.parseStringAttribute(parser, ATTR_DISABLED_MESSAGE);
         disabledMessageResId = ShortcutService.parseIntAttribute(parser,
                 ATTR_DISABLED_MESSAGE_RES_ID);
+        disabledMessageResName = ShortcutService.parseStringAttribute(parser,
+                ATTR_DISABLED_MESSAGE_RES_NAME);
         intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
         rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
         lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
         flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
-        iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
+        iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
+        iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
         bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
 
         final int outerDepth = parser.getDepth();
@@ -1167,10 +1250,11 @@
 
         return new ShortcutInfo(
                 userId, id, packageName, activityComponent, /* icon =*/ null,
-                title, titleResId, text, textResId, disabledMessage, disabledMessageResId,
+                title, titleResId, titleResName, text, textResId, textResName,
+                disabledMessage, disabledMessageResId, disabledMessageResName,
                 categories, intent,
                 intentPersistableExtras, rank, extras, lastChangedTimestamp, flags,
-                iconRes, bitmapPath);
+                iconResId, iconResName, bitmapPath);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 3eda13e..470d4af 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -221,6 +221,8 @@
                 | ShortcutInfo.FLAG_IMMUTABLE
                 | ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
 
+        // Note we don't need to set resource names here yet.  They'll be set when they're about
+        // to be published.
         return new ShortcutInfo(
                 userId,
                 id,
@@ -229,10 +231,13 @@
                 null, // icon
                 null, // title string
                 titleResId,
+                null, // title res name
                 null, // text string
                 textResId,
+                null, // text res name
                 null, // disabled message string
                 disabledMessageResId,
+                null, // disabled message res name
                 categories,
                 intent,
                 null, // intent extras
@@ -241,6 +246,7 @@
                 service.injectCurrentTimeMillis(),
                 flags,
                 iconResId,
+                null, // icon res name
                 null); // bitmap path
     }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 7835231..5769402 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -42,6 +42,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
@@ -113,12 +114,16 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
  * TODO:
+ * - Deal with the async nature of PACKAGE_ADD.  Basically when a publisher does anything after
+ *   it's upgraded, the manager should make sure the upgrade process has been executed.
+ *
  * - HandleUnlockUser needs to be async.  Wait on it in onCleanupUser.
  *
  * - Implement reportShortcutUsed().
@@ -316,10 +321,12 @@
         int LAUNCHER_PERMISSION_CHECK = 4;
         int CLEANUP_DANGLING_BITMAPS = 5;
         int GET_ACTIVITIES_WITH_METADATA = 6;
-        int GET_INSTALLED_APPLICATIONS = 7;
+        int GET_INSTALLED_PACKAGES = 7;
         int CHECK_PACKAGE_CHANGES = 8;
+        int GET_APPLICATION_RESOURCES = 9;
+        int RESOURCE_NAME_LOOKUP = 10;
 
-        int COUNT = CHECK_PACKAGE_CHANGES + 1;
+        int COUNT = RESOURCE_NAME_LOOKUP + 1;
     }
 
     final Object mStatLock = new Object();
@@ -1265,6 +1272,24 @@
         return scaledBitmap;
     }
 
+    /**
+     * For a shortcut, update all resource names from resource IDs, and also update all
+     * resource-based strings.
+     */
+    void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
+        final Resources publisherRes = injectGetResourcesForApplicationAsUser(
+                si.getPackage(), si.getUserId());
+        if (publisherRes != null) {
+            final long start = injectElapsedRealtime();
+            try {
+                si.lookupAndFillInResourceNames(publisherRes);
+            } finally {
+                logDurationStat(Stats.RESOURCE_NAME_LOOKUP, start);
+            }
+            si.resolveResourceStrings(publisherRes);
+        }
+    }
+
     // === Caller validation ===
 
     private boolean isCallerSystem() {
@@ -1327,7 +1352,8 @@
         throw new SecurityException("Calling package name mismatch");
     }
 
-    void postToHandler(Runnable r) {
+    // Overridden in unit tests to execute r synchronously.
+    void injectPostToHandler(Runnable r) {
         mHandler.post(r);
     }
 
@@ -1370,7 +1396,7 @@
         } finally {
             injectRestoreCallingIdentity(token);
         }
-        postToHandler(() -> {
+        injectPostToHandler(() -> {
             final ArrayList<ShortcutChangeListener> copy;
             synchronized (mLock) {
                 copy = new ArrayList<>(mListeners);
@@ -1537,12 +1563,18 @@
                         // TODO When activity is changing, check the dynamic count.
                     }
 
-                    // Note copyNonNullFieldsFrom() does the "udpatable with?" check too.
+                    // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
                     target.copyNonNullFieldsFrom(source);
 
                     if (replacingIcon) {
                         saveIconAndFixUpShortcut(userId, target);
                     }
+
+                    // When we're updating any resource related fields, re-extract the res names and
+                    // the values.
+                    if (replacingIcon || source.hasStringResources()) {
+                        fixUpShortcutResourceNamesAndValues(target);
+                    }
                 }
             }
         }
@@ -1980,21 +2012,6 @@
                     });
                 }
             }
-            // Resolve all strings if needed.
-            if (!cloneKeyFieldOnly) {
-                final long token = injectClearCallingIdentity();
-                try {
-                    for (int i = ret.size() - 1; i >= 0; i--) {
-                        try {
-                            ret.get(i).resolveStringsRequiringCrossUser(mContext);
-                        } catch (NameNotFoundException e) {
-                            continue;
-                        }
-                    }
-                } finally {
-                    injectRestoreCallingIdentity(token);
-                }
-            }
             return ret;
         }
 
@@ -2217,11 +2234,25 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
                 }
-                postToHandler(() -> scheduleSaveBaseState());
+                injectPostToHandler(() -> handleLocaleChanged());
             }
         }
     }
 
+    void handleLocaleChanged() {
+        if (DEBUG) {
+            Slog.d(TAG, "handleLocaleChanged");
+        }
+        scheduleSaveBaseState();
+
+        final long token = injectClearCallingIdentity();
+        try {
+            forEachLoadedUserLocked(u -> u.forAllPackages(p -> p.resolveResourceStrings()));
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
+    }
+
     /**
      * Package event callbacks.
      */
@@ -2282,11 +2313,17 @@
                         cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
                     }
                 }
+                final long now = injectCurrentTimeMillis();
 
                 // Then for each installed app, publish manifest shortcuts when needed.
-                forInstalledApplications(ownerUserId, ai -> {
+                forUpdatedPackages(ownerUserId, user.getLastAppScanTime(), ai -> {
                     user.handlePackageAddedOrUpdated(ai.packageName);
                 });
+
+                // Write the time just before the scan, because there may be apps that have just
+                // been updated, and we want to catch them in the next time.
+                user.setLastAppScanTime(now);
+                scheduleSaveUser(ownerUserId);
             }
         } finally {
             logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
@@ -2424,12 +2461,12 @@
 
     @Nullable
     @VisibleForTesting
-    List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) {
+    List<PackageInfo> injectInstalledPackages(@UserIdInt int userId) {
         final long start = injectElapsedRealtime();
         final long token = injectClearCallingIdentity();
         try {
-            final ParceledListSlice<ApplicationInfo> parceledList =
-                    mIPackageManager.getInstalledApplications(PACKAGE_MATCH_FLAGS, userId);
+            final ParceledListSlice<PackageInfo> parceledList =
+                    mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId);
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -2441,18 +2478,25 @@
         } finally {
             injectRestoreCallingIdentity(token);
 
-            logDurationStat(Stats.GET_INSTALLED_APPLICATIONS, start);
+            logDurationStat(Stats.GET_INSTALLED_PACKAGES, start);
         }
     }
 
-    private void forInstalledApplications(@UserIdInt int userId,
+    private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime,
             Consumer<ApplicationInfo> callback) {
-        final List<ApplicationInfo> list = injectInstalledApplications(userId);
+        if (DEBUG) {
+            Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime);
+        }
+        final List<PackageInfo> list = injectInstalledPackages(userId);
         for (int i = list.size() - 1; i >= 0; i--) {
-            final ApplicationInfo ai = list.get(i);
+            final PackageInfo pi = list.get(i);
 
-            if ((ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                callback.accept(ai);
+            if (((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0)
+                    && (pi.lastUpdateTime >= lastScanTime)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Found updated package " + pi.packageName);
+                }
+                callback.accept(pi.applicationInfo);
             }
         }
     }
@@ -2468,10 +2512,26 @@
 
     @Nullable
     XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
-// TODO Doesn't seem like ACROSS_USER is needed, but double check.
         return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
     }
 
+    @Nullable
+    Resources injectGetResourcesForApplicationAsUser(String packageName, int userId) {
+        final long start = injectElapsedRealtime();
+        final long token = injectClearCallingIdentity();
+        try {
+            return mContext.getPackageManager().getResourcesForApplicationAsUser(
+                    packageName, userId);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Resources for package " + packageName + " not found");
+            return null;
+        } finally {
+            injectRestoreCallingIdentity(token);
+
+            logDurationStat(Stats.GET_APPLICATION_RESOURCES, start);
+        }
+    }
+
     // === Backup & restore ===
 
     boolean shouldBackupApp(String packageName, int userId) {
@@ -2612,8 +2672,10 @@
                 dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
                 dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
                 dumpStatLS(pw, p, Stats.GET_ACTIVITIES_WITH_METADATA, "getActivities+metadata");
-                dumpStatLS(pw, p, Stats.GET_INSTALLED_APPLICATIONS, "getInstalledApplications");
+                dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
                 dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
+                dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
+                dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
             }
 
             for (int i = 0; i < mUsers.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 840c3df..f8ee325 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -52,6 +52,7 @@
 
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no";
+    private static final String ATTR_LAST_APP_SCAN_TIME = "last-app-scan-time";
 
     static final class PackageWithUser {
         final int userId;
@@ -107,6 +108,8 @@
 
     private long mKnownLocaleChangeSequenceNumber;
 
+    private long mLastAppScanTime;
+
     public ShortcutUser(ShortcutService service, int userId) {
         mService = service;
         mUserId = userId;
@@ -116,6 +119,14 @@
         return mUserId;
     }
 
+    public long getLastAppScanTime() {
+        return mLastAppScanTime;
+    }
+
+    public void setLastAppScanTime(long lastAppScanTime) {
+        mLastAppScanTime = lastAppScanTime;
+    }
+
     // We don't expose this directly to non-test code because only ShortcutUser should add to/
     // remove from it.
     @VisibleForTesting
@@ -258,6 +269,8 @@
 
         ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER,
                 mKnownLocaleChangeSequenceNumber);
+        ShortcutService.writeAttr(out, ATTR_LAST_APP_SCAN_TIME,
+                mLastAppScanTime);
 
         ShortcutService.writeTagValue(out, TAG_LAUNCHER,
                 mDefaultLauncherComponent);
@@ -299,6 +312,13 @@
         ret.mKnownLocaleChangeSequenceNumber = ShortcutService.parseLongAttribute(parser,
                 ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER);
 
+        // If lastAppScanTime is in the future, that means the clock went backwards.
+        // Just scan all apps again.
+        final long lastAppScanTime = ShortcutService.parseLongAttribute(parser,
+                ATTR_LAST_APP_SCAN_TIME);
+        final long currentTime = s.injectCurrentTimeMillis();
+        ret.mLastAppScanTime = lastAppScanTime < currentTime ? lastAppScanTime : 0;
+
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -361,6 +381,8 @@
         pw.print(mUserId);
         pw.print("  Known locale seq#: ");
         pw.print(mKnownLocaleChangeSequenceNumber);
+        pw.print("  Last app scan: ");
+        pw.print(mLastAppScanTime);
         pw.println();
 
         prefix += prefix + "  ";
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5920b2f..e0cfe4e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2589,15 +2589,8 @@
             final PhoneWindow win = new PhoneWindow(context);
             win.setIsStartingWindow(true);
 
-            final WindowManager.LayoutParams params = win.getAttributes();
             final Resources r = context.getResources();
-            CharSequence label = r.getText(labelRes);
-            // Only change the accessibility title if the label is localized
-            if (label != null) {
-                win.setTitle(label, true);
-            } else {
-                win.setTitle(nonLocalizedLabel, false);
-            }
+            win.setTitle(r.getText(labelRes, nonLocalizedLabel));
 
             win.setType(
                 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
@@ -2631,6 +2624,7 @@
             win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
                     WindowManager.LayoutParams.MATCH_PARENT);
 
+            final WindowManager.LayoutParams params = win.getAttributes();
             params.token = appToken;
             params.packageName = packageName;
             params.windowAnimations = win.getWindowStyle().getResourceId(
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8be5dfb..101f56f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1203,6 +1203,9 @@
             window.layer = windowState.mLayer;
             window.token = windowState.mClient.asBinder();
             window.title = windowState.mAttrs.accessibilityTitle;
+            if (window.title == null) {
+                window.title = windowState.mAttrs.getTitle();
+            }
             window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
 
             WindowState attachedWindow = windowState.mAttachedWindow;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 3b4c3cf..e46ed8d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -230,6 +230,14 @@
 
     boolean mForceScaleUntilResize;
 
+    // WindowState.mHScale and WindowState.mVScale contain the
+    // scale according to client specified layout parameters (e.g.
+    // one layout size, with another surface size, creates such scaling).
+    // Here we track an additional scaling factor used to follow stack
+    // scaling (as in the case of the Pinned stack animation).
+    float mExtraHScale = (float) 1.0;
+    float mExtraVScale = (float) 1.0;
+
     private final Rect mTmpSize = new Rect();
 
     WindowStateAnimator(final WindowState win) {
@@ -1397,8 +1405,8 @@
         mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
         calculateSurfaceBounds(w, w.getAttrs());
 
-        float extraHScale = (float) 1.0;
-        float extraVScale = (float) 1.0;
+        mExtraHScale = (float) 1.0;
+        mExtraVScale = (float) 1.0;
 
         // Once relayout has been called at least once, we need to make sure
         // we only resize the client surface during calls to relayout. For
@@ -1429,23 +1437,23 @@
             float surfaceHeight = mSurfaceController.getHeight();
             // We want to calculate the scaling based on the content area, not based on
             // the entire surface, so that we scale in sync with windows that don't have insets.
-            extraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
-            extraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
+            mExtraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
+            mExtraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
 
             // In the case of ForceScaleToCrop we scale entire tasks together,
             // and so we need to scale our offsets relative to the task bounds
             // or parent and child windows would fall out of alignment.
-            int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - extraHScale));
-            int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - extraVScale));
+            int posX = (int) (mTmpSize.left - w.mAttrs.x * (1 - mExtraHScale));
+            int posY = (int) (mTmpSize.top - w.mAttrs.y * (1 - mExtraVScale));
             // Imagine we are scaling down. As we scale the buffer down, we decrease the
             // distance between the surface top left, and the start of the surface contents
             // (previously it was surfaceInsets.left pixels in screen space but now it
-            // will be surfaceInsets.left*extraHScale). This means in order to keep the
+            // will be surfaceInsets.left*mExtraHScale). This means in order to keep the
             // non inset content at the same position, we have to shift the whole window
             // forward. Likewise for scaling up, we've increased this distance, and we need
             // to shift by a negative number to compensate.
-            posX += w.getAttrs().surfaceInsets.left * (1 - extraHScale);
-            posY += w.getAttrs().surfaceInsets.top * (1 - extraVScale);
+            posX += w.getAttrs().surfaceInsets.left * (1 - mExtraHScale);
+            posY += w.getAttrs().surfaceInsets.top * (1 - mExtraVScale);
 
             mSurfaceController.setPositionInTransaction(posX, posY, recoveringMemory);
 
@@ -1469,10 +1477,10 @@
 
         updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
 
-        mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * extraHScale,
-                mDtDx * w.mVScale * extraVScale,
-                mDsDy * w.mHScale * extraHScale,
-                mDtDy * w.mVScale * extraVScale, recoveringMemory);
+        mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
+                mDtDx * w.mVScale * mExtraVScale,
+                mDsDy * w.mHScale * mExtraHScale,
+                mDtDy * w.mVScale * mExtraVScale, recoveringMemory);
 
         if (mSurfaceResized) {
             mReportSurfaceResized = true;
@@ -1558,8 +1566,10 @@
 
             boolean prepared =
                 mSurfaceController.prepareToShowInTransaction(mShownAlpha, mAnimLayer,
-                        mDsDx * w.mHScale, mDtDx * w.mVScale,
-                        mDsDy * w.mHScale, mDtDy * w.mVScale,
+                        mDsDx * w.mHScale * mExtraHScale,
+                        mDtDx * w.mVScale * mExtraVScale,
+                        mDsDy * w.mHScale * mExtraHScale,
+                        mDtDy * w.mVScale * mExtraVScale,
                         recoveringMemory);
 
             if (prepared && mLastHidden && mDrawState == HAS_DRAWN) {
diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
new file mode 100644
index 0000000..808f4dd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.mock.MockContentProvider;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Fake for system settings.
+ *
+ * To use, ensure that the Context used by the test code returns a ContentResolver that uses this
+ * provider for the Settings authority:
+ *
+ *   class MyTestContext extends MockContext {
+ *       ...
+ *       private final MockContentResolver mContentResolver;
+ *       public MyTestContext(...) {
+ *           ...
+ *           mContentResolver = new MockContentResolver();
+ *           mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ *       }
+ *       ...
+ *       @Override
+ *       public ContentResolver getContentResolver() {
+ *           return mContentResolver;
+ *       }
+ *
+ * As long as the code under test is using the test Context, the actual code under test does not
+ * need to be modified, and can access Settings using the normal static methods:
+ *
+ *   Settings.Global.getInt(cr, "my_setting", 0);  // Returns 0.
+ *   Settings.Global.putInt(cr, "my_setting", 5);
+ *   Settings.Global.getInt(cr, "my_setting", 0);  // Returns 5.
+ *
+ * Note that this class cannot be used in the same process as real settings. This is because it
+ * works by passing an alternate ContentResolver to Settings operations. Unfortunately, the Settings
+ * class only fetches the content provider from the passed-in ContentResolver the first time it's
+ * used, and after that stores it in a per-process static.
+ *
+ * TODO: evaluate implementing settings change notifications. This would require:
+ *
+ * 1. Making ContentResolver#registerContentObserver non-final and overriding it in
+ *    MockContentResolver.
+ * 2. Making FakeSettingsProvider take a ContentResolver argument.
+ * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change.
+ */
+public class FakeSettingsProvider extends MockContentProvider {
+
+    private static final String TAG = FakeSettingsProvider.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final String[] TABLES = { "system", "secure", "global" };
+
+    private final HashMap<String, HashMap<String, String>> mTables = new HashMap<>();
+
+    public FakeSettingsProvider() {
+        for (int i = 0; i < TABLES.length; i++) {
+            mTables.put(TABLES[i], new HashMap<String, String>());
+        }
+    }
+
+    private Uri getUriFor(String table, String key) {
+        switch (table) {
+            case "system":
+                return Settings.System.getUriFor(key);
+            case "secure":
+                return Settings.Secure.getUriFor(key);
+            case "global":
+                return Settings.Global.getUriFor(key);
+            default:
+                throw new UnsupportedOperationException("Unknown settings table " + table);
+        }
+    }
+
+    public Bundle call(String method, String arg, Bundle extras) {
+        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+        String[] commands = method.split("_", 2);
+        String op = commands[0];
+        String table = commands[1];
+
+        Bundle out = new Bundle();
+        String value;
+        switch (op) {
+            case "GET":
+                value = mTables.get(table).get(arg);
+                if (value != null) {
+                    if (DBG) {
+                        Log.d(TAG, String.format("Returning fake setting %s.%s = %s",
+                                table, arg, value));
+                    }
+                    out.putString(Settings.NameValueTable.VALUE, value);
+                }
+                break;
+            case "PUT":
+                value = extras.getString(Settings.NameValueTable.VALUE, null);
+                if (DBG) {
+                    Log.d(TAG, String.format("Inserting fake setting %s.%s = %s",
+                            table, arg, value));
+                }
+                if (value != null) {
+                    mTables.get(table).put(arg, value);
+                } else {
+                    mTables.get(table).remove(arg);
+                }
+                break;
+            default:
+                throw new UnsupportedOperationException("Unknown command " + method);
+        }
+
+        return out;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
new file mode 100644
index 0000000..a18737c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Unit tests for FakeSettingsProvider.
+ */
+public class FakeSettingsProviderTest extends AndroidTestCase {
+
+    private MockContentResolver mCr;
+
+    @Override
+    public void setUp() throws Exception {
+        mCr = new MockContentResolver();
+        mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+    }
+
+    @SmallTest
+    public void testBasicOperation() throws Exception {
+        String settingName = Settings.System.SCREEN_BRIGHTNESS;
+
+        try {
+            Settings.System.getInt(mCr, settingName);
+            fail("FakeSettingsProvider should start off empty.");
+        } catch (Settings.SettingNotFoundException expected) {}
+
+        // Check that fake settings can be written and read back.
+        Settings.System.putInt(mCr, settingName, 123);
+        assertEquals(123, Settings.System.getInt(mCr, settingName));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 4fae4a7..68a35f1 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -26,6 +26,7 @@
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
@@ -61,12 +62,15 @@
 import android.os.MessageQueue.IdleHandler;
 import android.os.Process;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.LogPrinter;
 
+import com.android.internal.util.FakeSettingsProvider;
 import com.android.internal.util.WakeupMessage;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
@@ -118,27 +122,24 @@
     }
 
     private class MockContext extends BroadcastInterceptingContext {
+        private final MockContentResolver mContentResolver;
+
         MockContext(Context base) {
             super(base);
+            mContentResolver = new MockContentResolver();
+            mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         }
 
         @Override
-        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            // PendingIntents sent by the AlarmManager are not intercepted by
-            // BroadcastInterceptingContext so we must really register the receiver.
-            // This shouldn't effect the real NetworkMonitors as the action contains a random token.
-            if (filter.getAction(0).startsWith("android.net.netmon.lingerExpired")) {
-                return getBaseContext().registerReceiver(receiver, filter);
-            } else {
-                return super.registerReceiver(receiver, filter);
-            }
-        }
-
-        @Override
-        public Object getSystemService (String name) {
+        public Object getSystemService(String name) {
             if (name == Context.CONNECTIVITY_SERVICE) return mCm;
             return super.getSystemService(name);
         }
+
+        @Override
+        public ContentResolver getContentResolver() {
+            return mContentResolver;
+        }
     }
 
     /**
@@ -641,7 +642,6 @@
         public void waitForIdle() {
             waitForIdle(TIMEOUT_MS);
         }
-
     }
 
     private interface Criteria {
@@ -1477,6 +1477,73 @@
         defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
     }
 
+    @SmallTest
+    public void testMobileDataAlwaysOn() throws Exception {
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+        final HandlerThread handlerThread = new HandlerThread("MobileDataAlwaysOnFactory");
+        handlerThread.start();
+        NetworkCapabilities filter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_INTERNET);
+        final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "testFactory", filter);
+        testFactory.setScoreFilter(40);
+
+        // Register the factory and expect it to start looking for a network.
+        testFactory.expectAddRequests(1);
+        testFactory.register();
+        testFactory.waitForNetworkRequests(1);
+        assertTrue(testFactory.getMyStartRequested());
+
+        // Bring up wifi. The factory stops looking for a network.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        testFactory.expectAddRequests(2);  // Because the default request changes score twice.
+        mWiFiNetworkAgent.connect(true);
+        testFactory.waitForNetworkRequests(1);
+        assertFalse(testFactory.getMyStartRequested());
+
+        ContentResolver cr = mServiceContext.getContentResolver();
+
+        // Turn on mobile data always on. The factory starts looking again.
+        testFactory.expectAddRequests(1);
+        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 1);
+        mService.updateMobileDataAlwaysOn();
+        testFactory.waitForNetworkRequests(2);
+        assertTrue(testFactory.getMyStartRequested());
+
+        // Bring up cell data and check that the factory stops looking.
+        assertEquals(1, mCm.getAllNetworks().length);
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        testFactory.expectAddRequests(2);  // Because the cell request changes score twice.
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE);
+        testFactory.waitForNetworkRequests(2);
+        assertFalse(testFactory.getMyStartRequested());  // Because the cell network outscores us.
+
+        // Check that cell data stays up.
+        mService.waitForIdle();
+        verifyActiveNetwork(TRANSPORT_WIFI);
+        assertEquals(2, mCm.getAllNetworks().length);
+
+        // Turn off mobile data always on and expect the request to disappear...
+        testFactory.expectRemoveRequests(1);
+        Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, 0);
+        mService.updateMobileDataAlwaysOn();
+        testFactory.waitForNetworkRequests(1);
+
+        // ...  and cell data to be torn down.
+        cellNetworkCallback.expectCallback(CallbackState.LOST);
+        assertEquals(1, mCm.getAllNetworks().length);
+
+        testFactory.unregister();
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+        handlerThread.quit();
+    }
+
     private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
 
         public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index ed53b77..8ce7304 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -22,6 +22,8 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -92,6 +94,7 @@
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiPredicate;
@@ -302,8 +305,8 @@
         }
 
         @Override
-        List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) {
-            return getInstalledApplications(userId);
+        List<PackageInfo> injectInstalledPackages(@UserIdInt int userId) {
+            return getInstalledPackages(userId);
         }
 
         @Override
@@ -317,7 +320,7 @@
         }
 
         @Override
-        void postToHandler(Runnable r) {
+        void injectPostToHandler(Runnable r) {
             final long token = mContext.injectClearCallingIdentity();
             r.run();
             mContext.injectRestoreCallingIdentity(token);
@@ -441,6 +444,8 @@
 
     protected boolean mInjectedIsLowRamDevice;
 
+    protected Locale mInjectedLocale = Locale.ENGLISH;
+
     protected int mInjectedCallingUid;
     protected String mInjectedClientPackage;
 
@@ -597,6 +602,9 @@
         // User 0 is always running.
         when(mMockUserManager.isUserRunning(eq(USER_0))).thenAnswer(new AnswerIsUserRunning(true));
 
+        setUpAppResources();
+
+        // Start the service.
         initService();
         setCaller(CALLING_PACKAGE_1);
 
@@ -624,6 +632,53 @@
         }
     }
 
+    protected void setUpAppResources() throws Exception {
+        setUpAppResources(/* offset = */ 0);
+    }
+
+    protected void setUpAppResources(int ressIdOffset) throws Exception {
+        // ressIdOffset is used to adjust resource IDs to emulate the case where an updated app
+        // has resource IDs changed.
+
+        doAnswer(pmInvocation -> {
+            assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
+
+            final String packageName = (String) pmInvocation.getArguments()[0];
+            final int userId = (Integer) pmInvocation.getArguments()[1];
+
+            final Resources res = mock(Resources.class);
+
+            doAnswer(resInvocation -> {
+                final int argResId = (Integer) resInvocation.getArguments()[0];
+
+                return "string-" + packageName + "-user:" + userId + "-res:" + argResId
+                        + "/" + mInjectedLocale;
+            }).when(res).getString(anyInt());
+
+            doAnswer(resInvocation -> {
+                final int resId = (Integer) resInvocation.getArguments()[0];
+
+                // Always use the "string" resource type.  The type doesn't matter during the test.
+                return packageName + ":string/r" + resId;
+            }).when(res).getResourceName(anyInt());
+
+            doAnswer(resInvocation -> {
+                final String argResName = (String) resInvocation.getArguments()[0];
+                final String argType = (String) resInvocation.getArguments()[1];
+                final String argPackageName = (String) resInvocation.getArguments()[2];
+
+                // See the above code.  getResourceName() will just use "r" + res ID as the entry
+                // name.
+                String entryName = argResName;
+                if (entryName.contains("/")) {
+                    entryName = ShortcutInfo.getResourceEntryName(entryName);
+                }
+                return Integer.parseInt(entryName.substring(1)) + ressIdOffset;
+            }).when(res).getIdentifier(anyString(), anyString(), anyString());
+            return res;
+        }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
+    }
+
     protected static UserInfo withProfileGroupId(UserInfo in, int groupId) {
         in.profileGroupId = groupId;
         return in;
@@ -667,8 +722,16 @@
         mLauncherApps = null;
         mLauncherAppsMap.clear();
 
-        // Load the setting file.
+        // Send boot sequence events.
         mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
+
+        // Make sure a call to onSystemLocaleChangedNoLock() before PHASE_BOOT_COMPLETED will be
+        // ignored.
+        final long origSequenceNumber = mService.getLocaleChangeSequenceNumber();
+        mInternal.onSystemLocaleChangedNoLock();
+        assertEquals(origSequenceNumber, mService.getLocaleChangeSequenceNumber());
+
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
     }
 
     protected void shutdownServices() {
@@ -793,6 +856,27 @@
         return ret;
     }
 
+    private void addPackageInfo(PackageInfo pi, List<PackageInfo> list) {
+        if (pi != null) {
+            list.add(pi);
+        }
+    }
+
+    private List<PackageInfo> getInstalledPackages(int userId) {
+        final ArrayList<PackageInfo> ret = new ArrayList<>();
+
+        addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_2, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_3, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(CALLING_PACKAGE_4, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(LAUNCHER_1, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(LAUNCHER_2, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(LAUNCHER_3, userId, false), ret);
+        addPackageInfo(getInjectedPackageInfo(LAUNCHER_4, userId, false), ret);
+
+        return ret;
+    }
+
     protected void addManifestShortcutResource(ComponentName activity, int resId) {
         final String packageName = activity.getPackageName();
         LinkedHashMap<ComponentName, Integer> map = mActivityMetadataResId.get(packageName);
@@ -870,6 +954,18 @@
         setCaller(previousPackage, previousUserId);
     }
 
+    protected void runWithSystemUid(Runnable r) {
+        final int origUid = mInjectedCallingUid;
+        mInjectedCallingUid = Process.SYSTEM_UID;
+        r.run();
+        mInjectedCallingUid = origUid;
+    }
+
+    protected void lookupAndFillInResourceNames(ShortcutInfo si) {
+        runWithSystemUid(() -> si.lookupAndFillInResourceNames(
+                mService.injectGetResourcesForApplicationAsUser(si.getPackage(), si.getUserId())));
+    }
+
     protected int getCallingUserId() {
         return UserHandle.getUserId(mInjectedCallingUid);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c11be7a..fe2d1ec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -95,6 +95,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Tests for ShortcutService and ShortcutManager.
@@ -922,6 +923,7 @@
             ShortcutInfo s = getCallerShortcut("s2");
             assertTrue(s.hasIconResource());
             assertEquals(R.drawable.black_32x32, s.getIconResourceId());
+            assertEquals("string/r" + R.drawable.black_32x32, s.getIconResName());
             assertEquals("Title-s2", s.getTitle());
 
             s = getCallerShortcut("s4");
@@ -1091,21 +1093,6 @@
     }
 
     public void testGetShortcuts_resolveStrings() throws Exception {
-        doAnswer(pmInvocation -> {
-            assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
-
-            final String packageName = (String) pmInvocation.getArguments()[0];
-            final int userId = (Integer) pmInvocation.getArguments()[1];
-
-            final Resources res = mock(Resources.class);
-            doAnswer(resInvocation -> {
-                final int resId = (Integer) resInvocation.getArguments()[0];
-
-                return "string-" + packageName + "-user:" + userId + "-res:" + resId;
-            }).when(res).getString(anyInt());
-            return res;
-        }).when(mMockPackageManager).getResourcesForApplicationAsUser(anyString(), anyInt());
-
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
                     .setId("id")
@@ -1137,18 +1124,18 @@
             List<ShortcutInfo> ret = assertShortcutIds(
                     assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_0)),
                     "id");
-            assertEquals("string-com.android.test.1-user:0-res:10", ret.get(0).getTitle());
-            assertEquals("string-com.android.test.1-user:0-res:11", ret.get(0).getText());
-            assertEquals("string-com.android.test.1-user:0-res:12",
+            assertEquals("string-com.android.test.1-user:0-res:10/en", ret.get(0).getTitle());
+            assertEquals("string-com.android.test.1-user:0-res:11/en", ret.get(0).getText());
+            assertEquals("string-com.android.test.1-user:0-res:12/en",
                     ret.get(0).getDisabledMessage());
 
             // USER P0
             ret = assertShortcutIds(
                     assertAllStringsResolved(mLauncherApps.getShortcuts(q, HANDLE_USER_P0)),
                     "id");
-            assertEquals("string-com.android.test.1-user:20-res:10", ret.get(0).getTitle());
-            assertEquals("string-com.android.test.1-user:20-res:11", ret.get(0).getText());
-            assertEquals("string-com.android.test.1-user:20-res:12",
+            assertEquals("string-com.android.test.1-user:20-res:10/en", ret.get(0).getTitle());
+            assertEquals("string-com.android.test.1-user:20-res:11/en", ret.get(0).getText());
+            assertEquals("string-com.android.test.1-user:20-res:12/en",
                     ret.get(0).getDisabledMessage());
         });
     }
@@ -3602,6 +3589,84 @@
                 findShortcut(shortcuts.getValue(), "s1").getLastChangedTimestamp());
     }
 
+    /**
+     * Test the case where an updated app has resource IDs changed.
+     */
+    public void testHandlePackageUpdate_resIdChanged() throws Exception {
+        final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
+        final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
+
+        // Set up shortcuts.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            // Note resource strings are not officially supported (they're hidden), but
+            // should work.
+
+            final ShortcutInfo s1 = new ShortcutInfo.Builder(mClientContext)
+                    .setId("s1")
+                    .setActivity(makeComponent(ShortcutActivity.class))
+                    .setIntent(new Intent(Intent.ACTION_VIEW))
+                    .setIcon(icon1)
+                    .setTitleResId(10000)
+                    .setTextResId(10001)
+                    .setDisabledMessageResId(10002)
+                    .build();
+
+            final ShortcutInfo s2 = new ShortcutInfo.Builder(mClientContext)
+                    .setId("s2")
+                    .setActivity(makeComponent(ShortcutActivity.class))
+                    .setIntent(new Intent(Intent.ACTION_VIEW))
+                    .setIcon(icon2)
+                    .setTitleResId(20000)
+                    .build();
+
+            assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
+        });
+
+        // Verify.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            final ShortcutInfo s1 = getCallerShortcut("s1");
+            final ShortcutInfo s2 = getCallerShortcut("s2");
+
+            assertEquals(1000, s1.getIconResourceId());
+            assertEquals(10000, s1.getTitleResId());
+            assertEquals(10001, s1.getTextResId());
+            assertEquals(10002, s1.getDisabledMessageResourceId());
+
+            assertEquals(1001, s2.getIconResourceId());
+            assertEquals(20000, s2.getTitleResId());
+            assertEquals(0, s2.getTextResId());
+            assertEquals(0, s2.getDisabledMessageResourceId());
+        });
+
+        mService.saveDirtyInfo();
+        initService();
+
+        // Set up the mock resources again, with an "adjustment".
+        // When the package is updated, the service will fetch the updated res-IDs with res-names,
+        // and the new IDs will have this offset.
+        setUpAppResources(10);
+
+        // Update the package.
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            final ShortcutInfo s1 = getCallerShortcut("s1");
+            final ShortcutInfo s2 = getCallerShortcut("s2");
+
+            assertEquals(1010, s1.getIconResourceId());
+            assertEquals(10010, s1.getTitleResId());
+            assertEquals(10011, s1.getTextResId());
+            assertEquals(10012, s1.getDisabledMessageResourceId());
+
+            assertEquals(1011, s2.getIconResourceId());
+            assertEquals(20010, s2.getTitleResId());
+            assertEquals(0, s2.getTextResId());
+            assertEquals(0, s2.getDisabledMessageResourceId());
+        });
+    }
+
     protected void prepareForBackupTest() {
 
         prepareCrossProfileDataSet();
@@ -4785,27 +4850,35 @@
             assertEquals(R.drawable.icon1, si.getIconResourceId());
             assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                     si.getActivity());
+
             assertEquals(R.string.shortcut_title1, si.getTitleResId());
+            assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
             assertEquals(R.string.shortcut_text1, si.getTextResId());
+            assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
             assertEquals(R.string.shortcut_disabled_message1, si.getDisabledMessageResourceId());
+            assertEquals("r" + R.string.shortcut_disabled_message1, si.getDisabledMessageResName());
+
             assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
                     si.getCategories());
             assertEquals("action1", si.getIntent().getAction());
             assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
-            assertEquals(0, si.getRank());
 
             // check another
             si = getCallerShortcut("ms2");
 
             assertEquals("ms2", si.getId());
             assertEquals(R.drawable.icon2, si.getIconResourceId());
+
             assertEquals(R.string.shortcut_title2, si.getTitleResId());
+            assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
             assertEquals(R.string.shortcut_text2, si.getTextResId());
+            assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
             assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResourceId());
+            assertEquals("r" + R.string.shortcut_disabled_message2, si.getDisabledMessageResName());
+
             assertEquals(set("android.shortcut.conversation"), si.getCategories());
             assertEquals("action2", si.getIntent().getAction());
             assertEquals(null, si.getIntent().getData());
-            assertEquals(1, si.getRank());
 
             // check another
             si = getCallerShortcut("ms3");
@@ -4813,12 +4886,113 @@
             assertEquals("ms3", si.getId());
             assertEquals(0, si.getIconResourceId());
             assertEquals(R.string.shortcut_title1, si.getTitleResId());
+            assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
+
             assertEquals(0, si.getTextResId());
+            assertEquals(null, si.getTextResName());
             assertEquals(0, si.getDisabledMessageResourceId());
+            assertEquals(null, si.getDisabledMessageResName());
+
             assertEquals(null, si.getCategories());
             assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
             assertEquals(null, si.getIntent().getData());
-            assertEquals(2, si.getRank());
+        });
+    }
+
+    public void testManifestShortcuts_localeChange() {
+        mService.handleUnlockUser(USER_0);
+
+        // Package 1 updated, which has one valid manifest shortcut and one invalid.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_2);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            mManager.setDynamicShortcuts(list(makeShortcutWithTitle("s1", "title")));
+
+            assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+                    mManager.getManifestShortcuts()))),
+                    "ms1", "ms2");
+
+            // check first shortcut.
+            ShortcutInfo si = getCallerShortcut("ms1");
+
+            assertEquals("ms1", si.getId());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/en",
+                    si.getTitle());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/en",
+                    si.getText());
+            assertEquals("string-com.android.test.1-user:0-res:"
+                            + R.string.shortcut_disabled_message1 + "/en",
+                    si.getDisabledMessage());
+            assertEquals(START_TIME, si.getLastChangedTimestamp());
+
+            // check another
+            si = getCallerShortcut("ms2");
+
+            assertEquals("ms2", si.getId());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/en",
+                    si.getTitle());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/en",
+                    si.getText());
+            assertEquals("string-com.android.test.1-user:0-res:"
+                            + R.string.shortcut_disabled_message2 + "/en",
+                    si.getDisabledMessage());
+            assertEquals(START_TIME, si.getLastChangedTimestamp());
+
+            // Check the dynamic one.
+            si = getCallerShortcut("s1");
+
+            assertEquals("s1", si.getId());
+            assertEquals("title", si.getTitle());
+            assertEquals(null, si.getText());
+            assertEquals(null, si.getDisabledMessage());
+            assertEquals(START_TIME, si.getLastChangedTimestamp());
+        });
+
+        mInjectedCurrentTimeMillis++;
+
+        mInjectedLocale = Locale.JAPANESE;
+        mInternal.onSystemLocaleChangedNoLock();
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            // check first shortcut.
+            ShortcutInfo si = getCallerShortcut("ms1");
+
+            assertEquals("ms1", si.getId());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title1 + "/ja",
+                    si.getTitle());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text1 + "/ja",
+                    si.getText());
+            assertEquals("string-com.android.test.1-user:0-res:"
+                            + R.string.shortcut_disabled_message1 + "/ja",
+                    si.getDisabledMessage());
+            assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
+
+            // check another
+            si = getCallerShortcut("ms2");
+
+            assertEquals("ms2", si.getId());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_title2 + "/ja",
+                    si.getTitle());
+            assertEquals("string-com.android.test.1-user:0-res:" + R.string.shortcut_text2 + "/ja",
+                    si.getText());
+            assertEquals("string-com.android.test.1-user:0-res:"
+                            + R.string.shortcut_disabled_message2 + "/ja",
+                    si.getDisabledMessage());
+            assertEquals(START_TIME + 1, si.getLastChangedTimestamp());
+
+            // Check the dynamic one.  (locale change shouldn't affect.)
+            si = getCallerShortcut("s1");
+
+            assertEquals("s1", si.getId());
+            assertEquals("title", si.getTitle());
+            assertEquals(null, si.getText());
+            assertEquals(null, si.getDisabledMessage());
+            assertEquals(START_TIME, si.getLastChangedTimestamp()); // Not changed.
         });
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 1702ca4..399fddf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -32,6 +32,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ShortcutInfo;
+import android.content.res.Resources;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.os.PersistableBundle;
@@ -40,7 +41,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.frameworks.servicestests.R;
-import com.android.server.SystemService;
 
 /**
  * Tests for ShortcutService and ShortcutManager.
@@ -138,6 +138,13 @@
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
+
+        assertEquals(0, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
+        assertEquals(0, si.getTextResId());
+        assertEquals(null, si.getTextResName());
+        assertEquals(0, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
     }
 
     public void testShortcutInfoParcel_resId() {
@@ -163,6 +170,8 @@
         si.setBitmapPath("abc");
         si.setIconResourceId(456);
 
+        lookupAndFillInResourceNames(si);
+
         si = parceled(si);
 
         assertEquals(getTestContext().getPackageName(), si.getPackage());
@@ -170,8 +179,11 @@
         assertEquals(new ComponentName("a", "b"), si.getActivity());
         assertEquals(123, si.getIcon().getResId());
         assertEquals(10, si.getTitleResId());
+        assertEquals("r10", si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals("r11", si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals("r12", si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
@@ -181,6 +193,7 @@
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
+        assertEquals("string/r456", si.getIconResName());
     }
 
     public void testShortcutInfoClone() {
@@ -204,6 +217,8 @@
         sorig.setBitmapPath("abc");
         sorig.setIconResourceId(456);
 
+        lookupAndFillInResourceNames(sorig);
+
         ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
 
         assertEquals(USER_11, si.getUserId());
@@ -224,6 +239,7 @@
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
+        assertEquals("string/r456", si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
 
@@ -244,6 +260,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
@@ -263,6 +280,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
 
@@ -282,6 +300,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
     }
 
     public void testShortcutInfoClone_resId() {
@@ -305,6 +324,8 @@
         sorig.setBitmapPath("abc");
         sorig.setIconResourceId(456);
 
+        lookupAndFillInResourceNames(sorig);
+
         ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
 
         assertEquals(USER_11, si.getUserId());
@@ -314,8 +335,11 @@
         assertEquals(new ComponentName("a", "b"), si.getActivity());
         assertEquals(123, si.getIcon().getResId());
         assertEquals(10, si.getTitleResId());
+        assertEquals("r10", si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals("r11", si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals("r12", si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
@@ -325,6 +349,7 @@
         assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
         assertEquals("abc", si.getBitmapPath());
         assertEquals(456, si.getIconResourceId());
+        assertEquals("string/r456", si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
 
@@ -333,8 +358,11 @@
         assertEquals(new ComponentName("a", "b"), si.getActivity());
         assertEquals(null, si.getIcon());
         assertEquals(10, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals(null, si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
@@ -345,6 +373,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
 
@@ -353,8 +382,11 @@
         assertEquals(new ComponentName("a", "b"), si.getActivity());
         assertEquals(null, si.getIcon());
         assertEquals(10, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals(null, si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(123, si.getRank());
@@ -364,6 +396,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
 
         si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
 
@@ -372,8 +405,11 @@
         assertEquals(null, si.getActivity());
         assertEquals(null, si.getIcon());
         assertEquals(0, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
         assertEquals(0, si.getTextResId());
+        assertEquals(null, si.getTextResName());
         assertEquals(0, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
         assertEquals(null, si.getCategories());
         assertEquals(null, si.getIntent());
         assertEquals(0, si.getRank());
@@ -383,6 +419,7 @@
         assertEquals(null, si.getBitmapPath());
 
         assertEquals(456, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
     }
 
     public void testShortcutInfoClone_minimum() {
@@ -445,6 +482,8 @@
         sorig.setBitmapPath("abc");
         sorig.setIconResourceId(456);
 
+        lookupAndFillInResourceNames(sorig);
+
         ShortcutInfo si;
 
         si = sorig.clone(/* flags=*/ 0);
@@ -458,6 +497,9 @@
                 .setIcon(Icon.createWithResource(mClientContext, 456)).build());
         assertEquals("text", si.getText());
         assertEquals(456, si.getIcon().getResId());
+        assertEquals(0, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
+        assertEquals(null, si.getBitmapPath());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -586,6 +628,9 @@
                 .setIcon(Icon.createWithResource(mClientContext, 456)).build());
         assertEquals(11, si.getTextResId());
         assertEquals(456, si.getIcon().getResId());
+        assertEquals(0, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
+        assertEquals(null, si.getBitmapPath());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -593,6 +638,7 @@
         assertEquals(11, si.getTextResId());
         assertEquals("xyz", si.getTitle());
         assertEquals(0, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -600,6 +646,7 @@
         assertEquals(11, si.getTextResId());
         assertEquals(null, si.getTitle());
         assertEquals(123, si.getTitleResId());
+        assertEquals(null, si.getTitleResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -607,6 +654,7 @@
         assertEquals(123, si.getRank());
         assertEquals("xxx", si.getText());
         assertEquals(0, si.getTextResId());
+        assertEquals(null, si.getTextResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -614,6 +662,7 @@
         assertEquals(123, si.getRank());
         assertEquals(null, si.getText());
         assertEquals(1111, si.getTextResId());
+        assertEquals(null, si.getTextResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -621,6 +670,7 @@
         assertEquals(123, si.getRank());
         assertEquals("xxx", si.getDisabledMessage());
         assertEquals(0, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -628,6 +678,7 @@
         assertEquals(123, si.getRank());
         assertEquals(null, si.getDisabledMessage());
         assertEquals(11111, si.getDisabledMessageResourceId());
+        assertEquals(null, si.getDisabledMessageResName());
 
         si = sorig.clone(/* flags=*/ 0);
         si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -731,7 +782,8 @@
         assertEquals(123, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE
+                | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
         assertNotNull(si.getBitmapPath()); // Something should be set.
         assertEquals(0, si.getIconResourceId());
         assertTrue(si.getLastChangedTimestamp() < now);
@@ -740,15 +792,14 @@
     public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
-        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
-                getTestContext().getResources(), R.drawable.black_32x32));
+        final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
         ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
-                .setIcon(bmp32x32)
+                .setIcon(res32x32)
                 .setTitleResId(10)
                 .setTextResId(11)
                 .setDisabledMessageResId(12)
@@ -778,17 +829,21 @@
         assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
         assertEquals(null, si.getIcon());
         assertEquals(10, si.getTitleResId());
+        assertEquals("r10", si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals("r11", si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals("r12", si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
-        assertNotNull(si.getBitmapPath()); // Something should be set.
-        assertEquals(0, si.getIconResourceId());
+        assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES
+                | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+        assertNull(si.getBitmapPath());
+        assertEquals(R.drawable.black_32x32, si.getIconResourceId());
         assertTrue(si.getLastChangedTimestamp() < now);
     }
 
@@ -840,7 +895,7 @@
         assertEquals(123, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
         assertNull(si.getBitmapPath()); // No icon.
         assertEquals(0, si.getIconResourceId());
     }
@@ -848,15 +903,14 @@
     public void testShortcutInfoSaveAndLoad_forBackup_resId() {
         setCaller(CALLING_PACKAGE_1, USER_0);
 
-        final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
-                getTestContext().getResources(), R.drawable.black_32x32));
+        final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
 
         PersistableBundle pb = new PersistableBundle();
         pb.putInt("k", 1);
         ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
                 .setId("id")
                 .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
-                .setIcon(bmp32x32)
+                .setIcon(res32x32)
                 .setTitleResId(10)
                 .setTextResId(11)
                 .setDisabledMessageResId(12)
@@ -885,20 +939,23 @@
         assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
         assertEquals(null, si.getIcon());
         assertEquals(10, si.getTitleResId());
+        assertEquals("r10", si.getTitleResName());
         assertEquals(11, si.getTextResId());
+        assertEquals("r11", si.getTextResName());
         assertEquals(12, si.getDisabledMessageResourceId());
+        assertEquals("r12", si.getDisabledMessageResName());
         assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
         assertEquals("action", si.getIntent().getAction());
         assertEquals("val", si.getIntent().getStringExtra("key"));
         assertEquals(123, si.getRank());
         assertEquals(1, si.getExtras().getInt("k"));
 
-        assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+        assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
         assertNull(si.getBitmapPath()); // No icon.
         assertEquals(0, si.getIconResourceId());
+        assertEquals(null, si.getIconResName());
     }
 
-
     public void testThrottling() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
 
@@ -1086,11 +1143,6 @@
 
         final long origSequenceNumber = mService.getLocaleChangeSequenceNumber();
 
-        // onSystemLocaleChangedNoLock before boot completed will be ignored.
-        mInternal.onSystemLocaleChangedNoLock();
-        assertEquals(origSequenceNumber, mService.getLocaleChangeSequenceNumber());
-
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
         mInternal.onSystemLocaleChangedNoLock();
         assertEquals(origSequenceNumber + 1, mService.getLocaleChangeSequenceNumber());
 
@@ -1467,4 +1519,103 @@
 
         });
     }
+
+    // Test for a ShortcutInfo method.
+    public void testGetResourcePackageName() {
+        assertEquals(null, ShortcutInfo.getResourcePackageName(""));
+        assertEquals(null, ShortcutInfo.getResourcePackageName("abc"));
+        assertEquals("p", ShortcutInfo.getResourcePackageName("p:"));
+        assertEquals("p", ShortcutInfo.getResourcePackageName("p:xx"));
+        assertEquals("pac", ShortcutInfo.getResourcePackageName("pac:"));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testGetResourceTypeName() {
+        assertEquals(null, ShortcutInfo.getResourceTypeName(""));
+        assertEquals(null, ShortcutInfo.getResourceTypeName(":"));
+        assertEquals(null, ShortcutInfo.getResourceTypeName("/"));
+        assertEquals(null, ShortcutInfo.getResourceTypeName("/:"));
+        assertEquals("a", ShortcutInfo.getResourceTypeName(":a/"));
+        assertEquals("type", ShortcutInfo.getResourceTypeName("xxx:type/yyy"));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testGetResourceTypeAndEntryName() {
+        assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName(""));
+        assertEquals(null, ShortcutInfo.getResourceTypeAndEntryName("abc"));
+        assertEquals("", ShortcutInfo.getResourceTypeAndEntryName("p:"));
+        assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName(":x"));
+        assertEquals("x", ShortcutInfo.getResourceTypeAndEntryName("p:x"));
+        assertEquals("xyz", ShortcutInfo.getResourceTypeAndEntryName("pac:xyz"));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testGetResourceEntryName() {
+        assertEquals(null, ShortcutInfo.getResourceEntryName(""));
+        assertEquals(null, ShortcutInfo.getResourceEntryName("ab:"));
+        assertEquals("", ShortcutInfo.getResourceEntryName("/"));
+        assertEquals("abc", ShortcutInfo.getResourceEntryName("/abc"));
+        assertEquals("abc", ShortcutInfo.getResourceEntryName("xyz/abc"));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testLookUpResourceName_systemResources() {
+        // For android system resources, lookUpResourceName will simply return the value as a
+        // string, regardless of "withType".
+        final Resources res = getTestContext().getResources();
+
+        assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
+                android.R.string.cancel, true, getTestContext().getPackageName()));
+        assertEquals("" + android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceName(res,
+                android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
+        assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
+                android.R.string.cancel, false, getTestContext().getPackageName()));
+    }
+
+    public void testLookUpResourceName_appResources() {
+        final Resources res = getTestContext().getResources();
+
+        assertEquals("shortcut_text1", ShortcutInfo.lookUpResourceName(res,
+                R.string.shortcut_text1, false, getTestContext().getPackageName()));
+        assertEquals("string/shortcut_text1", ShortcutInfo.lookUpResourceName(res,
+                R.string.shortcut_text1, true, getTestContext().getPackageName()));
+
+        assertEquals("black_16x64", ShortcutInfo.lookUpResourceName(res,
+                R.drawable.black_16x64, false, getTestContext().getPackageName()));
+        assertEquals("drawable/black_16x64", ShortcutInfo.lookUpResourceName(res,
+                R.drawable.black_16x64, true, getTestContext().getPackageName()));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testLookUpResourceId_systemResources() {
+        final Resources res = getTestContext().getResources();
+
+        assertEquals(android.R.string.cancel, ShortcutInfo.lookUpResourceId(res,
+                "" + android.R.string.cancel, null,
+                getTestContext().getPackageName()));
+        assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
+                "" + android.R.drawable.alert_dark_frame, null,
+                getTestContext().getPackageName()));
+    }
+
+    // Test for a ShortcutInfo method.
+    public void testLookUpResourceId_appResources() {
+        final Resources res = getTestContext().getResources();
+
+        assertEquals(R.string.shortcut_text1,
+                ShortcutInfo.lookUpResourceId(res, "shortcut_text1", "string",
+                        getTestContext().getPackageName()));
+
+        assertEquals(R.string.shortcut_text1,
+                ShortcutInfo.lookUpResourceId(res, "string/shortcut_text1", null,
+                        getTestContext().getPackageName()));
+
+        assertEquals(R.drawable.black_16x64,
+                ShortcutInfo.lookUpResourceId(res, "black_16x64", "drawable",
+                        getTestContext().getPackageName()));
+
+        assertEquals(R.drawable.black_16x64,
+                ShortcutInfo.lookUpResourceId(res, "drawable/black_16x64", null,
+                        getTestContext().getPackageName()));
+    }
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1ccacc2..3a6d1fd 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -307,6 +307,11 @@
          */
         public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
 
+        /**
+         * Indicates that the call has CDMA Enhanced Voice Privacy enabled.
+         */
+        public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 0x00000080;
+
         //******************************************************************************************
         // Next PROPERTY value: 0x00000100
         //******************************************************************************************
@@ -463,6 +468,9 @@
             if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
                 builder.append(" PROPERTY_IS_EXTERNAL_CALL");
             }
+            if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+                builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a5e3c461..3316bc8 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -334,9 +334,13 @@
      */
     public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
 
+    /**
+     * Indicates that the connection has CDMA Enhanced Voice Privacy enabled.
+     */
+    public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 1<<5;
 
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<5
+    // Next PROPERTY value: 1<<6
     //**********************************************************************************************
 
     /**
@@ -531,6 +535,10 @@
             builder.append(" PROPERTY_IS_EXTERNAL_CALL");
         }
 
+        if (can(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+            builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
+        }
+
         builder.append("]");
         return builder.toString();
     }
diff --git a/telephony/java/android/telephony/TelephonyHistogram.java b/telephony/java/android/telephony/TelephonyHistogram.java
index 985102c..9d9ce72 100644
--- a/telephony/java/android/telephony/TelephonyHistogram.java
+++ b/telephony/java/android/telephony/TelephonyHistogram.java
@@ -274,7 +274,7 @@
         out.writeInt(minTimeMs);
         out.writeInt(maxTimeMs);
         out.writeInt(averageTimeMs);
-        out.writeLong(sampleCount);
+        out.writeInt(sampleCount);
         if (initialTimings == null) {
             out.writeInt(ABSENT);
         } else {