Merge "Import translations. DO NOT MERGE" into oc-dev
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 3221c5d..620e5cf 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -226,11 +226,7 @@
         @Override
         public void doAlarm(IAlarmCompleteListener alarmManager) {
             mCompletion = alarmManager;
-            mHandler.post(this);
-        }
 
-        @Override
-        public void run() {
             // Remove this listener from the wrapper cache first; the server side
             // already considers it gone
             synchronized (AlarmManager.class) {
@@ -239,6 +235,11 @@
                 }
             }
 
+            mHandler.post(this);
+        }
+
+        @Override
+        public void run() {
             // Now deliver it to the app
             try {
                 mListener.onAlarm();
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index f369955..4994fbb 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -39,8 +39,11 @@
      * Called whenever IActivityManager.startActivity is called on an activity that is already
      * running in the pinned stack and the activity is not actually started, but the task is either
      * brought to the front or a new Intent is delivered to it.
+     *
+     * @param clearedTask whether or not the launch activity also cleared the task as a part of
+     * starting
      */
-    void onPinnedActivityRestartAttempt();
+    void onPinnedActivityRestartAttempt(boolean clearedTask);
 
     /**
      * Called whenever the pinned stack is starting animating a resize.
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0265ea5..10d6a7b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -671,22 +671,22 @@
     }
     
     /**
-     * Make this service run in the foreground, supplying the ongoing
+     * If your service is started (running through {@link Context#startService(Intent)}), then
+     * also make this service run in the foreground, supplying the ongoing
      * notification to be shown to the user while in this state.
-     * By default services are background, meaning that if the system needs to
-     * kill them to reclaim more memory (such as to display a large page in a
-     * web browser), they can be killed without too much harm.  You can set this
-     * flag if killing your service would be disruptive to the user, such as
+     * By default started services are background, meaning that their process won't be given
+     * foreground CPU scheduling (unless something else in that process is foreground) and,
+     * if the system needs to kill them to reclaim more memory (such as to display a large page in a
+     * web browser), they can be killed without too much harm.  You use
+     * {@link #startForeground} if killing your service would be disruptive to the user, such as
      * if your service is performing background music playback, so the user
      * would notice if their music stopped playing.
-     * 
-     * <p>If you need your application to run on platform versions prior to API
-     * level 5, you can use the following model to call the the older setForeground()
-     * or this modern method as appropriate:
-     * 
-     * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
-     *   foreground_compatibility}
-     * 
+     *
+     * <p>Note that calling this method does <em>not</em> put the service in the started state
+     * itself, even though the name sounds like it.  You must always call
+     * {@link #startService(Intent)} first to tell the system it should keep the service running,
+     * and then use this method to tell it to keep it running harder.</p>
+     *
      * @param id The identifier for this notification as per
      * {@link NotificationManager#notify(int, Notification)
      * NotificationManager.notify(int, Notification)}; must not be 0.
@@ -716,7 +716,9 @@
 
     /**
      * Remove this service from foreground state, allowing it to be killed if
-     * more memory is needed.
+     * more memory is needed.  This does not stop the service from running (for that
+     * you use {@link #stopSelf()} or related methods), just takes it out of the
+     * foreground state.
      *
      * @param flags additional behavior options.
      * @see #startForeground(int, Notification)
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 307fc91..2df011f 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -39,7 +39,7 @@
     }
 
     @Override
-    public void onPinnedActivityRestartAttempt() throws RemoteException {
+    public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException {
     }
 
     @Override
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d60d4db..64c0f31 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1024,28 +1024,6 @@
     }
 
     /**
-     * enable or disable Bluetooth HCI snoop log.
-     *
-     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission
-     *
-     * @return true to indicate configure HCI log successfully, or false on
-     *         immediate error
-     * @hide
-     */
-    public boolean configHciSnoopLog(boolean enable) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) return mService.configHciSnoopLog(enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
-    }
-
-    /**
      * Factory reset bluetooth settings.
      *
      * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index b337817..43c5ae4 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -97,7 +97,6 @@
     ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in ParcelUuid uuid, int port, int flag);
     ParcelFileDescriptor createSocketChannel(int type, in String serviceName, in ParcelUuid uuid, int port, int flag);
 
-    boolean configHciSnoopLog(boolean enable);
     boolean factoryReset();
 
     boolean isMultiAdvertisementSupported();
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index ee56a18..4e53914 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -44,7 +44,6 @@
     public boolean notLaunched;
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
-    public boolean blockUninstall;
     public boolean instantApp;
     public int enabled;
     public String lastDisableAppCaller;
@@ -75,7 +74,6 @@
         notLaunched = o.notLaunched;
         hidden = o.hidden;
         suspended = o.suspended;
-        blockUninstall = o.blockUninstall;
         instantApp = o.instantApp;
         enabled = o.enabled;
         lastDisableAppCaller = o.lastDisableAppCaller;
@@ -193,9 +191,6 @@
         if (suspended != oldState.suspended) {
             return false;
         }
-        if (blockUninstall != oldState.blockUninstall) {
-            return false;
-        }
         if (instantApp != oldState.instantApp) {
             return false;
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7ead659..0840549 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5176,13 +5176,6 @@
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
         /**
-         * bluetooth HCI snoop log configuration
-         * @hide
-         */
-        public static final String BLUETOOTH_HCI_LOG =
-                "bluetooth_hci_log";
-
-        /**
          * @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
          */
         @Deprecated
@@ -10414,6 +10407,15 @@
         public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
 
         /**
+         * Displays toasts when an app posts a notification that does not specify a valid channel.
+         *
+         * The value 1 - enable, 0 - disable
+         * @hide
+         */
+        public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
+                "show_notification_channel_warnings";
+
+        /**
          * Whether cell is enabled/disabled
          * @hide
          */
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ad3a99d..b0d6395 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1404,6 +1404,11 @@
             // or double-clicks that could "dismiss" the floating toolbar.
             int delay = ViewConfiguration.getDoubleTapTimeout();
             mTextView.postDelayed(mShowFloatingToolbar, delay);
+
+            // This classifies the text and most likely returns before the toolbar is actually
+            // shown. If not, it will update the toolbar with the result when classification
+            // returns. We would rather not wait for a long running classification process.
+            invalidateActionModeAsync();
         }
     }
 
@@ -1853,7 +1858,7 @@
             mInsertionPointCursorController.invalidateHandle();
         }
         if (mTextActionMode != null) {
-            invalidateActionModeAsync();
+            invalidateActionMode();
         }
     }
 
@@ -1945,12 +1950,12 @@
                 if (mRestartActionModeOnNextRefresh) {
                     // To avoid distraction, newly start action mode only when selection action
                     // mode is being restarted.
-                    startSelectionActionMode();
+                    startSelectionActionModeAsync(false);
                 }
             } else if (selectionController == null || !selectionController.isActive()) {
                 // Insertion action mode is active. Avoid dismissing the selection.
                 stopTextActionModeWithPreservingSelection();
-                startSelectionActionMode();
+                startSelectionActionModeAsync(false);
             } else {
                 mTextActionMode.invalidateContentRect();
             }
@@ -2004,15 +2009,8 @@
     /**
      * Asynchronously starts a selection action mode using the TextClassifier.
      */
-    void startSelectionActionModeAsync() {
-        getSelectionActionModeHelper().startActionModeAsync();
-    }
-
-    /**
-     * Synchronously starts a selection action mode without the TextClassifier.
-     */
-    void startSelectionActionMode() {
-        getSelectionActionModeHelper().startActionMode();
+    void startSelectionActionModeAsync(boolean adjustSelection) {
+        getSelectionActionModeHelper().startActionModeAsync(adjustSelection);
     }
 
     /**
@@ -2022,6 +2020,15 @@
         getSelectionActionModeHelper().invalidateActionModeAsync();
     }
 
+    /**
+     * Synchronously invalidates an action mode without the TextClassifier.
+     */
+    private void invalidateActionMode() {
+        if (mTextActionMode != null) {
+            mTextActionMode.invalidate();
+        }
+    }
+
     private SelectionActionModeHelper getSelectionActionModeHelper() {
         if (mSelectionActionModeHelper == null) {
             mSelectionActionModeHelper = new SelectionActionModeHelper(this);
@@ -2075,7 +2082,7 @@
         }
         if (mTextActionMode != null) {
             // Text action mode is already started
-            invalidateActionModeAsync();
+            invalidateActionMode();
             return false;
         }
 
@@ -4703,7 +4710,7 @@
             }
             positionAtCursorOffset(offset, false);
             if (mTextActionMode != null) {
-                invalidateActionModeAsync();
+                invalidateActionMode();
             }
         }
 
@@ -4787,7 +4794,7 @@
             }
             updateDrawable();
             if (mTextActionMode != null) {
-                invalidateActionModeAsync();
+                invalidateActionMode();
             }
         }
 
@@ -5414,13 +5421,8 @@
                     resetDragAcceleratorState();
 
                     if (mTextView.hasSelection()) {
-                        // Do not invoke the text assistant if this was a drag selection.
-                        if (mHaventMovedEnoughToStartDrag) {
-                            startSelectionActionModeAsync();
-                        } else {
-                            startSelectionActionMode();
-                        }
-
+                        // Drag selection should not be adjusted by the text classifier.
+                        startSelectionActionModeAsync(mHaventMovedEnoughToStartDrag);
                     }
                     break;
             }
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index c9d172f..16a1087 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -65,7 +65,7 @@
                 textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
     }
 
-    public void startActionModeAsync() {
+    public void startActionModeAsync(boolean adjustSelection) {
         cancelAsyncTask();
         if (isNoOpTextClassifier() || !hasSelection()) {
             // No need to make an async call for a no-op TextClassifier.
@@ -74,16 +74,16 @@
         } else {
             resetTextClassificationHelper();
             mTextClassificationAsyncTask = new TextClassificationAsyncTask(
-                    mEditor.getTextView(), TIMEOUT_DURATION,
-                    mTextClassificationHelper::suggestSelection, this::startActionMode)
+                    mEditor.getTextView(),
+                    TIMEOUT_DURATION,
+                    adjustSelection
+                            ? mTextClassificationHelper::suggestSelection
+                            : mTextClassificationHelper::classifyText,
+                    this::startActionMode)
                     .execute();
         }
     }
 
-    public void startActionMode() {
-        startActionMode(null);
-    }
-
     public void invalidateActionModeAsync() {
         cancelAsyncTask();
         if (isNoOpTextClassifier() || !hasSelection()) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 242dcf5..eaf1115 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10579,7 +10579,7 @@
                         Selection.setSelection((Spannable) text, start, end);
                         // Make sure selection mode is engaged.
                         if (mEditor != null) {
-                            mEditor.startSelectionActionMode();
+                            mEditor.startSelectionActionModeAsync(false);
                         }
                         return true;
                     }
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
index b18f83c..eeb3854 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.icu.util.ULocale;
 import android.os.LocaleList;
+import android.text.TextUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -41,7 +42,7 @@
     /**
      * Calculates a matching score for the single desired locale.
      *
-     * @see LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])
+     * @see LocaleUtils#filterByLanguage(List, LocaleExtractor, LocaleList, ArrayList)
      *
      * @param supported The locale supported by IME subtype.
      * @param desired The locale preferred by user.
@@ -72,48 +73,6 @@
         return 3;
     }
 
-    /**
-     * Calculates a matching score for the desired locale list.
-     *
-     * <p>The supported locale gets a matching score of 3 if all language, script and country of the
-     * supported locale matches with the desired locale.  The supported locale gets a matching
-     * score of 2 if the language and script of the supported locale matches with the desired
-     * locale. The supported locale gets a matching score of 1 if only language of the supported
-     * locale matches with the desired locale.  The supported locale gets a matching score of 0 if
-     * the language of the supported locale doesn't match with the desired locale.</p>
-     *
-     * @param supported The locale supported by IME subtyle.
-     * @param desired The locale list preferred by user. Typically system locale list.
-     * @param out The output buffer to be stored the individual score for the desired language list.
-     * The length of {@code out} must be same as the length of {@code desired} language list.
-     * @return {@code false} if supported locale doesn't match with any desired locale list.
-     * Otherwise {@code true}.
-     */
-    private static boolean calculateMatchingScore(@NonNull final ULocale supported,
-            @NonNull final LocaleList desired, @NonNull byte[] out) {
-        if (desired.isEmpty()) {
-            return false;
-        }
-
-        boolean allZeros = true;
-        final int N = desired.size();
-        for (int i = 0; i < N; ++i) {
-            final Locale locale = desired.get(i);
-
-            if (!locale.getLanguage().equals(supported.getLanguage())) {
-                // TODO: cache the result of addLikelySubtags if it is slow.
-                out[i] = 0;
-            } else {
-                out[i] = calculateMatchingSubScore(
-                        supported, ULocale.addLikelySubtags(ULocale.forLocale(locale)));
-                if (allZeros && out[i] != 0) {
-                    allZeros = false;
-                }
-            }
-        }
-        return !allZeros;
-    }
-
     private static final class ScoreEntry implements Comparable<ScoreEntry> {
         public int mIndex = -1;
         @NonNull public final byte[] mScore;  // matching score of the i-th system languages.
@@ -175,17 +134,17 @@
     /**
      * Filters the given items based on language preferences.
      *
-     * <p>For each language found in {@code preferredLanguages}, this method tries to copy at most
+     * <p>For each language found in {@code preferredLocales}, this method tries to copy at most
      * one best-match item from {@code source} to {@code dest}.  For example, if
-     * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
+     * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLocales},
      * this method tries to copy at most one English locale, at most one Japanese, and at most one
      * French locale from {@code source} to {@code dest}.  Here the best matching English locale
      * will be searched from {@code source} based on matching score. For the score design, see
-     * {@link LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])}</p>
+     * {@link LocaleUtils#calculateMatchingSubScore(ULocale, ULocale)}</p>
      *
      * @param sources Source items to be filtered.
      * @param extractor Type converter from the source items to {@link Locale} object.
-     * @param preferredLanguages Ordered list of locales with which the input items will be
+     * @param preferredLocales Ordered list of locales with which the input items will be
      * filtered.
      * @param dest Destination into which the filtered items will be added.
      * @param <T> Type of the data items.
@@ -194,17 +153,43 @@
     public static <T> void filterByLanguage(
             @NonNull List<T> sources,
             @NonNull LocaleExtractor<T> extractor,
-            @NonNull LocaleList preferredLanguages,
+            @NonNull LocaleList preferredLocales,
             @NonNull ArrayList<T> dest) {
+        if (preferredLocales.isEmpty()) {
+            return;
+        }
+
+        final int numPreferredLocales = preferredLocales.size();
         final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
-        final byte[] score = new byte[preferredLanguages.size()];
+        final byte[] score = new byte[numPreferredLocales];
+        final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
 
         final int sourceSize = sources.size();
         for (int i = 0; i < sourceSize; ++i) {
             final Locale locale = extractor.get(sources.get(i));
-            if (locale == null ||
-                    !calculateMatchingScore(ULocale.addLikelySubtags(ULocale.forLocale(locale)),
-                            preferredLanguages, score)) {
+            if (locale == null) {
+                continue;
+            }
+
+            boolean canSkip = true;
+            for (int j = 0; j < numPreferredLocales; ++j) {
+                final Locale preferredLocale = preferredLocales.get(j);
+                if (!TextUtils.equals(locale.getLanguage(), preferredLocale.getLanguage())) {
+                    score[j] = 0;
+                    continue;
+                }
+                if (preferredULocaleCache[j] == null) {
+                    preferredULocaleCache[j] = ULocale.addLikelySubtags(
+                            ULocale.forLocale(preferredLocale));
+                }
+                score[j] = calculateMatchingSubScore(
+                        preferredULocaleCache[j],
+                        ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+                if (canSkip && score[j] != 0) {
+                    canSkip = false;
+                }
+            }
+            if (canSkip) {
                 continue;
             }
 
diff --git a/core/res/res/values-mcc204/config.xml b/core/res/res/values-mcc204/config.xml
new file mode 100644
index 0000000..790f768
--- /dev/null
+++ b/core/res/res/values-mcc204/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <bool name="config_use_sim_language_file">true</bool>
+
+</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
index 63431a4..3049488 100755
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -36,4 +36,7 @@
     <bool name="config_auto_attach_data_on_creation">false</bool>
 
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
+
+    <bool name="config_use_sim_language_file">true</bool>
+
 </resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index a210f5b..6f85081 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -62,4 +62,6 @@
 
     <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
 
+    <bool name="config_use_sim_language_file">true</bool>
+
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8006f78..aeb564b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1299,9 +1299,9 @@
              * Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action
                protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission.
 
-         This must be set to a valid network recommendation app.
+         This must be set to a valid network recommendation app or empty.
      -->
-    <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false">com.android.networkrecommendation</string>
+    <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
 
     <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
          replaced by an app at run-time. When disabled, only the
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e633d66..27f98b3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -644,8 +644,10 @@
     <!-- Text shown when viewing channel settings for notifications related to a usb connection -->
     <string name="notification_channel_usb">USB connection</string>
 
-    <!-- Text shown when viewing channel settings for notifications related to running foreground
-        services [CHAR LIMIT=NONE] -->
+    <!-- This is the label for the notification channel settings that controls the behavior
+        of the notification about applications that are running in the background (that is,
+        perhaps confusingly, running foreground services but not the foreground UI on the screen).
+        [CHAR LIMIT=NONE] -->
     <string name="notification_channel_foreground_service">Apps running in background</string>
 
     <!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE] -->
@@ -663,7 +665,10 @@
         data usage</string>
 
     <!-- Separator for foreground service notification content listing all apps when there
-        are multiple apps running [CHAR LIMIT=NONE] -->
+        are multiple apps running.  The left and right side may both already be compound
+        (constructed using this separator).  Should be kept as short as possible, this is
+        for summary text in the notification where there is not a lot of space.
+        [CHAR LIMIT=NONE] -->
     <string name="foreground_service_multiple_separator"><xliff:g id="left_side">%1$s</xliff:g>,
         <xliff:g id="right_side">%2$s</xliff:g></string>
 
@@ -2696,10 +2701,10 @@
     <string name="dial">Phone</string>
 
     <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
-    <string name="map">Map</string>
+    <string name="map">Maps</string>
 
     <!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
-    <string name="browse">Browse</string>
+    <string name="browse">Browser</string>
 
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 2ae2ca0..690b051 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -511,6 +511,8 @@
 
     <style name="Widget.CheckedTextView">
         <item name="textAlignment">viewStart</item>
+        <item name="breakStrategy">high_quality</item>
+        <item name="hyphenationFrequency">normal</item>
     </style>
 
     <style name="Widget.TextView.ListSeparator">
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
index 411a3f8..37b2a50 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothInstrumentation.java
@@ -72,8 +72,6 @@
             getAddress();
         } else if ("getBondedDevices".equals(command)) {
             getBondedDevices();
-        } else if ("enableBtSnoop".equals(command)) {
-            enableBtSnoop();
         } else {
             finish(null);
         }
@@ -116,12 +114,6 @@
         finish(mSuccessResult);
     }
 
-    public void enableBtSnoop() {
-        Assert.assertTrue("failed to enable snoop log",
-                getBluetoothAdapter().configHciSnoopLog(true));
-        finish(mSuccessResult);
-    }
-
     public void finish(Bundle result) {
         if (result == null) {
             result = new Bundle();
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index aeed20e..a977072 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -397,6 +397,7 @@
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                 Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
@@ -404,7 +405,6 @@
                  Settings.Secure.BACKUP_ENABLED,
                  Settings.Secure.BACKUP_PROVISIONED,
                  Settings.Secure.BACKUP_TRANSPORT,
-                 Settings.Secure.BLUETOOTH_HCI_LOG,
                  Settings.Secure.CARRIER_APPS_HANDLED,
                  Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
                  Settings.Secure.COMPLETED_CATEGORY_PREFIX,
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 5a178a5..eb513e1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -998,7 +998,8 @@
             if (mRssi != info.getRssi()) {
                 mRssi = info.getRssi();
                 updated = true;
-            } else if (mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
+            } else if (mNetworkInfo != null && networkInfo != null
+                    && mNetworkInfo.getDetailedState() != networkInfo.getDetailedState()) {
                 updated = true;
             }
             mInfo = info;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index f519a90..d4ce40c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -465,9 +465,9 @@
     private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException {
         // Updating scores can happen together or one after the other, so the latch countdown is set
         // to 2.
-        mAccessPointsChangedLatch = new CountDownLatch(3);
+        mAccessPointsChangedLatch = new CountDownLatch(2);
         updateScores();
-        assertTrue("onAccessPointChanged was not called three times",
+        assertTrue("onAccessPointChanged was not called twice",
             mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c7c2222..f475361 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -985,9 +985,6 @@
                 Settings.Secure.AUTOFILL_SERVICE,
                 SecureSettingsProto.AUTOFILL_SERVICE);
         dumpSetting(s, p,
-                Settings.Secure.BLUETOOTH_HCI_LOG,
-                SecureSettingsProto.BLUETOOTH_HCI_LOG);
-        dumpSetting(s, p,
                 Settings.Secure.USER_SETUP_COMPLETE,
                 SecureSettingsProto.USER_SETUP_COMPLETE);
         dumpSetting(s, p,
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bf70c5a..d15fcae 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2033,7 +2033,9 @@
     <!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
     <string name="running_foreground_services_title">Apps running in background</string>
 
-    <!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
+    <!-- Descriptive text of an item in the "running foreground services" dialog, telling the
+        user what will happen when they tap on that item (which is an application that has
+        been identified for them as running). [CHAR LIMIT=NONE] -->
     <string name="running_foreground_services_msg">Tap for details on battery and data usage</string>
 
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index 49e780c..9d286cf 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -66,7 +66,7 @@
     private DialogInterface.OnClickListener mAppClickListener =
             new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int which) {
-                    String pkg = mPackages[which];
+                    String pkg = mAdapter.getItem(which).packageName;
                     Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                     intent.setData(Uri.fromParts("package", pkg, null));
                     startActivity(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index df03fdc..bdc0871 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -107,12 +107,12 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt() {
+        public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             if (!checkCurrentUserId(false /* debug */)) {
                 return;
             }
 
-            mTouchHandler.getMotionHelper().expandPip();
+            mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index fc52a2e..5121c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -140,14 +140,25 @@
      * Resizes the pinned stack back to fullscreen.
      */
     void expandPip() {
+        expandPip(false /* skipAnimation */);
+    }
+
+    /**
+     * Resizes the pinned stack back to fullscreen.
+     */
+    void expandPip(boolean skipAnimation) {
         cancelAnimations();
         mHandler.post(() -> {
             try {
-                mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
-                        true /* allowResizeInDockedMode */, true /* preserveWindows */,
-                        true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+                if (skipAnimation) {
+                    mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
+                } else {
+                    mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
+                            true /* allowResizeInDockedMode */, true /* preserveWindows */,
+                            true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
+                }
             } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PiP menu activity", e);
+                Log.e(TAG, "Error expanding PiP activity", e);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 657f08b..9735bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,7 +625,7 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt() {
+        public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
             if (!checkCurrentUserId(DEBUG)) {
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a9e1f61..f431517 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -158,7 +158,7 @@
         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
         public void onActivityPinned(String packageName) { }
         public void onActivityUnpinned() { }
-        public void onPinnedActivityRestartAttempt() { }
+        public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
         public void onPinnedStackAnimationStarted() { }
         public void onPinnedStackAnimationEnded() { }
         public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
@@ -223,10 +223,11 @@
         }
 
         @Override
-        public void onPinnedActivityRestartAttempt()
+        public void onPinnedActivityRestartAttempt(boolean clearedTask)
                 throws RemoteException{
             mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
-            mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+            mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
+                    .sendToTarget();
         }
 
         @Override
@@ -1294,7 +1295,8 @@
                     }
                     case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+                            mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
+                                    msg.arg1 != 0);
                         }
                         break;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
index 82910b8..454edbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
@@ -34,7 +34,7 @@
  * A utility class to colorize bitmaps with a color gradient and a special blending mode
  */
 public class ImageGradientColorizer {
-    public Bitmap colorize(Drawable drawable, int backgroundColor) {
+    public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
         int width = drawable.getIntrinsicWidth();
         int height = drawable.getIntrinsicHeight();
         int size = Math.min(width, height);
@@ -70,8 +70,6 @@
                 0, 0, 0, 1, 0,
         });
 
-        drawable.setColorFilter(new ColorMatrixColorFilter(m));
-        drawable.draw(canvas);
         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
         LinearGradient linearGradient =  new LinearGradient(0, 0, size, 0,
                 new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
@@ -81,16 +79,27 @@
         Canvas fadeInCanvas = new Canvas(fadeIn);
         drawable.clearColorFilter();
         drawable.draw(fadeInCanvas);
+
+        if (isRtl) {
+            // Let's flip the gradient
+            fadeInCanvas.translate(size, 0);
+            fadeInCanvas.scale(-1, 1);
+        }
         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
         fadeInCanvas.drawPaint(paint);
+
+        Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
+        coloredPaint.setAlpha((int) (0.5f * 255));
+        canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
+
+        linearGradient =  new LinearGradient(0, 0, size, 0,
+                new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
+                new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
+        paint.setShader(linearGradient);
+        fadeInCanvas.drawPaint(paint);
         canvas.drawBitmap(fadeIn, 0, 0, null);
 
-        linearGradient = new LinearGradient(0, 0, size, 0,
-                new int[] {backgroundColor, Color.argb(0.5f, tr, tg, tb), 0},
-                new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
-        paint.setShader(linearGradient);
-        paint.setXfermode(null);
-        canvas.drawPaint(paint);
         return newBitmap;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index cef225b..52c053f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -25,6 +25,7 @@
 import android.graphics.drawable.Icon;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.graphics.Palette;
+import android.util.LayoutDirection;
 
 import com.android.systemui.R;
 
@@ -187,7 +188,9 @@
                         : R.color.notification_material_background_color;
                 backgroundColor = mContext.getColor(id);
             }
-            Bitmap colorized = mColorizer.colorize(drawable, backgroundColor);
+            Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
+                    mContext.getResources().getConfiguration().getLayoutDirection() ==
+                            LayoutDirection.RTL);
             builder.setLargeIcon(Icon.createWithBitmap(colorized));
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index d57f813..8934460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -28,17 +28,22 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.util.wakelock.WakeLockFake;
+import com.android.systemui.utils.hardware.FakeSensorManager;
 
 import org.junit.Before;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class DozeTriggersTest {
     private Context mContext;
     private DozeTriggers mTriggers;
@@ -46,7 +51,7 @@
     private DozeHostFake mHost;
     private AmbientDisplayConfiguration mConfig;
     private DozeParameters mParameters;
-    private SensorManagerFake mSensors;
+    private FakeSensorManager mSensors;
     private Handler mHandler;
     private WakeLock mWakeLock;
     private Instrumentation mInstrumentation;
@@ -65,7 +70,7 @@
         mHost = new DozeHostFake();
         mConfig = DozeConfigurationUtil.createMockConfig();
         mParameters = DozeConfigurationUtil.createMockParameters();
-        mSensors = new SensorManagerFake(mContext);
+        mSensors = new FakeSensorManager(mContext);
         mHandler = new Handler(Looper.getMainLooper());
         mWakeLock = new WakeLockFake();
 
@@ -76,29 +81,29 @@
     }
 
     @Test
-    @Ignore("setup crashes on virtual devices")
     public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
 
         mInstrumentation.runOnMainSync(()->{
             mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
-            mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.DOZE);
+            mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
 
             mHost.callback.onNotificationHeadsUp();
         });
 
         mInstrumentation.runOnMainSync(() -> {
-            mSensors.PROXIMITY.sendProximityResult(false); /* Near */
+            mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
         });
 
         verify(mMachine, never()).requestState(any());
+        verify(mMachine, never()).requestPulse(anyInt());
 
         mInstrumentation.runOnMainSync(()->{
             mHost.callback.onNotificationHeadsUp();
         });
 
         mInstrumentation.runOnMainSync(() -> {
-            mSensors.PROXIMITY.sendProximityResult(true); /* Far */
+            mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
         });
 
         verify(mMachine).requestPulse(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
similarity index 60%
rename from packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
rename to packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
index 5b4b891..30be665 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/SensorManagerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
@@ -11,10 +11,10 @@
  * 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.
+ * limitations under the License
  */
 
-package com.android.systemui.doze;
+package com.android.systemui.utils.hardware;
 
 import android.content.Context;
 import android.hardware.HardwareBuffer;
@@ -33,6 +33,8 @@
 import com.google.android.collect.Lists;
 
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -44,18 +46,38 @@
  * Note that this class ignores the "Handler" argument, so the test is responsible for calling the
  * listener on the right thread.
  */
-public class SensorManagerFake extends SensorManager {
+public class FakeSensorManager extends SensorManager {
 
-    public MockSensor PROXIMITY;
+    private final MockProximitySensor mMockProximitySensor;
 
-    public SensorManagerFake(Context context) {
-        PROXIMITY = new MockSensor(context.getSystemService(SensorManager.class)
-                .getDefaultSensor(Sensor.TYPE_PROXIMITY));
+    public FakeSensorManager(Context context) throws Exception {
+        Sensor proxSensor = context.getSystemService(SensorManager.class)
+                .getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        if (proxSensor == null) {
+            // No prox? Let's create a fake one!
+            proxSensor = createSensor(Sensor.TYPE_PROXIMITY);
+        }
+        mMockProximitySensor = new MockProximitySensor(proxSensor);
+    }
+
+    public MockProximitySensor getMockProximitySensor() {
+        return mMockProximitySensor;
+    }
+
+    @Override
+    public Sensor getDefaultSensor(int type) {
+        Sensor s = super.getDefaultSensor(type);
+        if (s != null) {
+            return s;
+        }
+        // Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
+        // return non-wakeup sensors if we can't find a wakeup sensor.
+        return getDefaultSensor(type, false /* wakeup */);
     }
 
     @Override
     protected List<Sensor> getFullSensorList() {
-        return Lists.newArrayList(PROXIMITY.sensor);
+        return Lists.newArrayList(mMockProximitySensor.sensor);
     }
 
     @Override
@@ -65,8 +87,8 @@
 
     @Override
     protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
-        if (sensor == PROXIMITY.sensor || sensor == null) {
-            PROXIMITY.listeners.remove(listener);
+        if (sensor == mMockProximitySensor.sensor || sensor == null) {
+            mMockProximitySensor.listeners.remove(listener);
         }
     }
 
@@ -74,8 +96,8 @@
     protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
             int delayUs,
             Handler handler, int maxReportLatencyUs, int reservedFlags) {
-        if (sensor == PROXIMITY.sensor) {
-            PROXIMITY.listeners.add(listener);
+        if (sensor == mMockProximitySensor.sensor) {
+            mMockProximitySensor.listeners.add(listener);
             return true;
         }
         return false;
@@ -141,11 +163,44 @@
         return false;
     }
 
-    public class MockSensor {
+    private Sensor createSensor(int type) throws Exception {
+        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+        constr.setAccessible(true);
+        Sensor sensor = constr.newInstance();
+
+        setSensorType(sensor, type);
+        setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+        setSensorField(sensor, "mVendor", "Mock Vendor");
+        setSensorField(sensor, "mVersion", 1);
+        setSensorField(sensor, "mHandle", -1);
+        setSensorField(sensor, "mMaxRange", 10);
+        setSensorField(sensor, "mResolution", 1);
+        setSensorField(sensor, "mPower", 1);
+        setSensorField(sensor, "mMinDelay", 1000);
+        setSensorField(sensor, "mMaxDelay", 1000000000);
+        setSensorField(sensor, "mFlags", 0);
+        setSensorField(sensor, "mId", -1);
+
+        return sensor;
+    }
+
+    private void setSensorField(Sensor sensor, String fieldName, Object value) throws Exception {
+        Field field = Sensor.class.getDeclaredField(fieldName);
+        field.setAccessible(true);
+        field.set(sensor, value);
+    }
+
+    private void setSensorType(Sensor sensor, int type) throws Exception {
+        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+        setter.setAccessible(true);
+        setter.invoke(sensor, type);
+    }
+
+    public class MockProximitySensor {
         final Sensor sensor;
         final ArraySet<SensorEventListener> listeners = new ArraySet<>();
 
-        private MockSensor(Sensor sensor) {
+        private MockProximitySensor(Sensor sensor) {
             this.sensor = sensor;
         }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index a68a7dd..d1a43d2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -774,7 +774,7 @@
     }
 
     // High level policy: apps are generally ineligible for backup if certain conditions apply
-    public static boolean appIsEligibleForBackup(ApplicationInfo app) {
+    public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) {
         // 1. their manifest states android:allowBackup="false"
         if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
             return false;
@@ -790,15 +790,18 @@
             return false;
         }
 
-        return true;
+        // Everything else checks out; the only remaining roadblock would be if the
+        // package were disabled
+        return !appIsDisabled(app, pm);
     }
 
-    // Checks if the app is in a stopped state, that means it won't receive broadcasts.
+    // Checks if the app is in a stopped state.  This is not part of the general "eligible for
+    // backup?" check because we *do* still need to restore data to apps in this state (e.g.
+    // newly-installing ones)
     private static boolean appIsStopped(ApplicationInfo app) {
         return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
     }
 
-    // We also avoid backups of 'disabled' apps
     private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
         switch (pm.getApplicationEnabledSetting(app.packageName)) {
             case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
@@ -1526,7 +1529,8 @@
                     foundApps.add(pkgName); // all apps that we've addressed already
                     try {
                         PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
-                        if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) {
+                        if (appGetsFullBackup(pkg)
+                                && appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)) {
                             schedule.add(new FullBackupEntry(pkgName, lastBackup));
                         } else {
                             if (DEBUG) {
@@ -1545,7 +1549,8 @@
                 // New apps can arrive "out of band" via OTA and similar, so we also need to
                 // scan to make sure that we're tracking all full-backup candidates properly
                 for (PackageInfo app : apps) {
-                    if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+                    if (appGetsFullBackup(app)
+                            && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
                         if (!foundApps.contains(app.packageName)) {
                             if (MORE_DEBUG) {
                                 Slog.i(TAG, "New full backup app " + app.packageName + " found");
@@ -1574,7 +1579,8 @@
             changed = true;
             schedule = new ArrayList<FullBackupEntry>(apps.size());
             for (PackageInfo info : apps) {
-                if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) {
+                if (appGetsFullBackup(info)
+                        && appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
                     schedule.add(new FullBackupEntry(info.packageName, 0));
                 }
             }
@@ -2032,7 +2038,8 @@
                 for (String packageName : pkgList) {
                     try {
                         PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
-                        if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
+                        if (appGetsFullBackup(app)
+                                && appIsEligibleForBackup(app.applicationInfo, mPackageManager)) {
                             enqueueFullBackup(packageName, now);
                             scheduleNextFullBackupJob(0);
                         } else {
@@ -2432,7 +2439,7 @@
             try {
                 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
                         PackageManager.GET_SIGNATURES);
-                if (!appIsEligibleForBackup(packageInfo.applicationInfo)) {
+                if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager)) {
                     sendBackupOnPackageResult(observer, packageName,
                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
                     continue;
@@ -2952,7 +2959,7 @@
             try {
                 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
                         PackageManager.GET_SIGNATURES);
-                if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
+                if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo, mPackageManager)) {
                     // The manifest has changed but we had a stale backup request pending.
                     // This won't happen again because the app won't be requesting further
                     // backups.
@@ -4399,7 +4406,7 @@
             Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
             while (iter.hasNext()) {
                 PackageInfo pkg = iter.next().getValue();
-                if (!appIsEligibleForBackup(pkg.applicationInfo)
+                if (!appIsEligibleForBackup(pkg.applicationInfo, mPackageManager)
                         || appIsStopped(pkg.applicationInfo)) {
                     iter.remove();
                     if (DEBUG) {
@@ -4681,7 +4688,7 @@
                     PackageInfo info = mPackageManager.getPackageInfo(pkg,
                             PackageManager.GET_SIGNATURES);
                     mCurrentPackage = info;
-                    if (!appIsEligibleForBackup(info.applicationInfo)) {
+                    if (!appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
                         // Cull any packages that have indicated that backups are not permitted,
                         // that run as system-domain uids but do not define their own backup agents,
                         // as well as any explicit mention of the 'special' shared-storage agent
@@ -8658,7 +8665,7 @@
                             continue;
                         }
 
-                        if (appIsEligibleForBackup(info.applicationInfo)) {
+                        if (appIsEligibleForBackup(info.applicationInfo, mPackageManager)) {
                             mAcceptSet.add(info);
                         }
                     } catch (NameNotFoundException e) {
@@ -10830,9 +10837,8 @@
         try {
             PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
                     PackageManager.GET_SIGNATURES);
-            if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
-                    appIsStopped(packageInfo.applicationInfo) ||
-                    appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
+            if (!appIsEligibleForBackup(packageInfo.applicationInfo, mPackageManager) ||
+                    appIsStopped(packageInfo.applicationInfo)) {
                 return false;
             }
             IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 09f240f..642b8bf 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -140,7 +140,7 @@
         int N = pkgs.size();
         for (int a = N-1; a >= 0; a--) {
             PackageInfo pkg = pkgs.get(a);
-            if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo)) {
+            if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) {
                 pkgs.remove(a);
             }
         }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 18b4571..c785fb9 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1494,16 +1494,6 @@
                             if (mGetNameAddressOnly) return;
                         }
 
-                        try {
-                            boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
-                                Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
-                            if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) {
-                                Slog.e(TAG,"IBluetooth.configHciSnoopLog return false");
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call configHciSnoopLog", e);
-                        }
-
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4313533..a5615a9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -166,8 +166,11 @@
      */
     static final class ActiveForegroundApp {
         String mPackageName;
+        int mUid;
         CharSequence mLabel;
         boolean mShownWhileScreenOn;
+        boolean mAppOnTop;
+        boolean mShownWhileTop;
         long mStartTime;
         long mStartVisibleTime;
         long mEndTime;
@@ -728,11 +731,12 @@
         synchronized (mAm) {
             final long now = SystemClock.elapsedRealtime();
             final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+            long nextUpdateTime = Long.MAX_VALUE;
             if (smap != null) {
                 for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
                     ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
                     if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) {
-                        if (aa.mEndTime < (aa.mStartVisibleTime
+                        if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime
                                 + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
                             // Check to see if this should still be displayed...  we continue
                             // until it has been shown for at least the timeout duration.
@@ -741,6 +745,12 @@
                                 smap.mActiveForegroundApps.removeAt(i);
                                 smap.mActiveForegroundAppsChanged = true;
                                 continue;
+                            } else {
+                                long hideTime = aa.mStartVisibleTime
+                                        + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
+                                if (hideTime < nextUpdateTime) {
+                                    nextUpdateTime = hideTime;
+                                }
                             }
                         } else {
                             // This was up for longer than the timeout, so just remove immediately.
@@ -749,10 +759,17 @@
                             continue;
                         }
                     }
-                    if (active == null) {
-                        active = new ArrayList<>();
+                    if (!aa.mAppOnTop) {
+                        if (active == null) {
+                            active = new ArrayList<>();
+                        }
+                        active.add(aa);
                     }
-                    active.add(aa);
+                }
+                smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
+                if (nextUpdateTime < Long.MAX_VALUE) {
+                    Message msg = smap.obtainMessage();
+                    smap.sendMessageAtTime(msg, nextUpdateTime);
                 }
             }
             if (!smap.mActiveForegroundAppsChanged) {
@@ -842,7 +859,7 @@
             active.mNumActive--;
             if (active.mNumActive <= 0) {
                 active.mEndTime = SystemClock.elapsedRealtime();
-                if (active.mEndTime >= (active.mStartVisibleTime
+                if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime
                         + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
                     // Have been active for long enough that we will remove it immediately.
                     smap.mActiveForegroundApps.remove(r.packageName);
@@ -887,6 +904,31 @@
         }
     }
 
+    void foregroundServiceProcStateChangedLocked(UidRecord uidRec) {
+        ServiceMap smap = mServiceMap.get(UserHandle.getUserId(uidRec.uid));
+        if (smap != null) {
+            boolean changed = false;
+            for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
+                ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
+                if (active.mUid == uidRec.uid) {
+                    if (uidRec.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                        if (!active.mAppOnTop) {
+                            active.mAppOnTop = true;
+                            changed = true;
+                        }
+                        active.mShownWhileTop = true;
+                    } else if (active.mAppOnTop) {
+                        active.mAppOnTop = false;
+                        changed = true;
+                    }
+                }
+            }
+            if (changed) {
+                requestUpdateActiveForegroundAppsLocked(smap, 0);
+            }
+        }
+    }
+
     private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
             Notification notification, int flags) {
         if (id != 0) {
@@ -948,7 +990,13 @@
                     if (active == null) {
                         active = new ActiveForegroundApp();
                         active.mPackageName = r.packageName;
+                        active.mUid = r.appInfo.uid;
                         active.mShownWhileScreenOn = mScreenOn;
+                        if (r.app != null) {
+                            active.mAppOnTop = active.mShownWhileTop =
+                                    r.app.uidRecord.curProcState
+                                            <= ActivityManager.PROCESS_STATE_TOP;
+                        }
                         active.mStartTime = active.mStartVisibleTime
                                 = SystemClock.elapsedRealtime();
                         smap.mActiveForegroundApps.put(r.packageName, active);
@@ -2790,6 +2838,9 @@
                 if (!doit && didSomething) {
                     return true;
                 }
+                if (doit && filterByClasses == null) {
+                    forceStopPackageLocked(packageName, mServiceMap.valueAt(i).mUserId);
+                }
             }
         } else {
             ServiceMap smap = mServiceMap.get(userId);
@@ -2798,6 +2849,9 @@
                 didSomething = collectPackageServicesLocked(packageName, filterByClasses,
                         evenPersistent, doit, killProcess, items);
             }
+            if (doit && filterByClasses == null) {
+                forceStopPackageLocked(packageName, userId);
+            }
         }
 
         if (mTmpCollectionResults != null) {
@@ -2806,10 +2860,11 @@
             }
             mTmpCollectionResults.clear();
         }
+
         return didSomething;
     }
 
-    void removeUninstalledPackageLocked(String packageName, int userId) {
+    void forceStopPackageLocked(String packageName, int userId) {
         ServiceMap smap = mServiceMap.get(userId);
         if (smap != null && smap.mActiveForegroundApps.size() > 0) {
             for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
@@ -3640,6 +3695,10 @@
                         }
                         pw.print("    mNumActive=");
                         pw.print(aa.mNumActive);
+                        pw.print(" mAppOnTop=");
+                        pw.print(aa.mAppOnTop);
+                        pw.print(" mShownWhileTop=");
+                        pw.print(aa.mShownWhileTop);
                         pw.print(" mShownWhileScreenOn=");
                         pw.println(aa.mShownWhileScreenOn);
                         pw.print("    mStartTime=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4d09313..ba48783 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -120,7 +120,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
@@ -5427,7 +5426,7 @@
     public static class DumpStackFileObserver extends FileObserver {
         // Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
         private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
-        static final int TRACE_DUMP_TIMEOUT_SECONDS = TRACE_DUMP_TIMEOUT_MS / 1000;
+        static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
 
         private final String mTracesPath;
         private boolean mClosed;
@@ -5443,21 +5442,41 @@
             notify();
         }
 
-        public void dumpWithTimeout(int pid) {
+        public long dumpWithTimeout(int pid, long timeout) {
             sendSignal(pid, SIGNAL_QUIT);
+            final long start = SystemClock.elapsedRealtime();
+
+            final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
             synchronized (this) {
                 try {
-                    wait(TRACE_DUMP_TIMEOUT_MS); // Wait for traces file to be closed.
+                    wait(waitTime); // Wait for traces file to be closed.
                 } catch (InterruptedException e) {
                     Slog.wtf(TAG, e);
                 }
             }
+
+            // This avoids a corner case of passing a negative time to the native
+            // trace in case we've already hit the overall timeout.
+            final long timeWaited = SystemClock.elapsedRealtime() - start;
+            if (timeWaited >= timeout) {
+                return timeWaited;
+            }
+
             if (!mClosed) {
                 Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
                        ". Attempting native stack collection.");
-                Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath, TRACE_DUMP_TIMEOUT_SECONDS);
+
+                final long nativeDumpTimeoutMs = Math.min(
+                        NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
+
+                Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
+                        (int) (nativeDumpTimeoutMs / 1000));
             }
+
+            final long end = SystemClock.elapsedRealtime();
             mClosed = false;
+
+            return (end - start);
         }
     }
 
@@ -5467,6 +5486,9 @@
         // Use a FileObserver to detect when traces finish writing.
         // The order of traces is considered important to maintain for legibility.
         DumpStackFileObserver observer = new DumpStackFileObserver(tracesPath);
+
+        // We must complete all stack dumps within 20 seconds.
+        long remainingTime = 20 * 1000;
         try {
             observer.startWatching();
 
@@ -5476,10 +5498,18 @@
                 for (int i = 0; i < num; i++) {
                     if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
                             + firstPids.get(i));
-                    final long sime = SystemClock.elapsedRealtime();
-                    observer.dumpWithTimeout(firstPids.get(i));
-                    if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i)
-                            + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+                    final long timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
+
+                    remainingTime -= timeTaken;
+                    if (remainingTime <= 0) {
+                        Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+                            "); deadline exceeded.");
+                        return;
+                    }
+
+                    if (DEBUG_ANR) {
+                        Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+                    }
                 }
             }
 
@@ -5487,12 +5517,24 @@
             if (nativePids != null) {
                 for (int pid : nativePids) {
                     if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
-                    final long sime = SystemClock.elapsedRealtime();
+                    final long nativeDumpTimeoutMs = Math.min(
+                            DumpStackFileObserver.NATIVE_DUMP_TIMEOUT_MS, remainingTime);
 
+                    final long start = SystemClock.elapsedRealtime();
                     Debug.dumpNativeBacktraceToFileTimeout(
-                            pid, tracesPath, DumpStackFileObserver.TRACE_DUMP_TIMEOUT_SECONDS);
-                    if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid
-                            + " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
+                            pid, tracesPath, (int) (nativeDumpTimeoutMs / 1000));
+                    final long timeTaken = SystemClock.elapsedRealtime() - start;
+
+                    remainingTime -= timeTaken;
+                    if (remainingTime <= 0) {
+                        Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
+                            "); deadline exceeded.");
+                        return;
+                    }
+
+                    if (DEBUG_ANR) {
+                        Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+                    }
                 }
             }
 
@@ -5516,12 +5558,20 @@
                     ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
                     if (lastPids.indexOfKey(stats.pid) >= 0) {
                         numProcs++;
-                        if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid "
-                                + stats.pid);
-                        final long stime = SystemClock.elapsedRealtime();
-                        observer.dumpWithTimeout(stats.pid);
-                        if (DEBUG_ANR) Slog.d(TAG, "Done with extra pid " + stats.pid
-                                + " in " + (SystemClock.elapsedRealtime()-stime) + "ms");
+
+                        if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+
+                        final long timeTaken = observer.dumpWithTimeout(stats.pid, remainingTime);
+                        remainingTime -= timeTaken;
+                        if (remainingTime <= 0) {
+                            Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + stats.pid +
+                                "); deadline exceeded.");
+                            return;
+                        }
+
+                        if (DEBUG_ANR) {
+                            Slog.d(TAG, "Done with extra pid " + stats.pid + " in " + timeTaken + "ms");
+                        }
                     } else if (DEBUG_ANR) {
                         Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
                                 + stats.pid);
@@ -19007,7 +19057,7 @@
 
                                         removeTasksByPackageNameLocked(ssp, userId);
 
-                                        mServices.removeUninstalledPackageLocked(ssp, userId);
+                                        mServices.forceStopPackageLocked(ssp, userId);
 
                                         // Hide the "unsupported display" dialog if necessary.
                                         if (mUnsupportedDisplaySizeDialog != null && ssp.equals(
@@ -19890,8 +19940,9 @@
     }
 
     /**
-     * NOTE: For the pinned stack, this method is only called after the bounds animation has
-     *       animated the stack to the fullscreen.
+     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
+     *       activity and clearing the task at the same time.
      */
     @Override
     public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
@@ -22277,6 +22328,9 @@
                         if (uidRec.curProcState > app.curProcState) {
                             uidRec.curProcState = app.curProcState;
                         }
+                        if (app.foregroundServices) {
+                            uidRec.foregroundServices = true;
+                        }
                     }
                 }
 
@@ -22518,6 +22572,9 @@
                 uidRec.setWhitelist = uidRec.curWhitelist;
                 enqueueUidChangeLocked(uidRec, -1, uidChange);
                 noteUidProcessState(uidRec.uid, uidRec.curProcState);
+                if (uidRec.foregroundServices) {
+                    mServices.foregroundServiceProcStateChangedLocked(uidRec);
+                }
             }
         }
         if (mLocalPowerManager != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 912b22c..d348224 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1659,21 +1659,13 @@
         }
 
         if (mStackId == DOCKED_STACK_ID) {
-            final ActivityRecord r = topStack.topRunningActivityLocked();
-
             // If the assistant stack is focused and translucent, then the docked stack is always
             // visible
             if (topStack.isAssistantStack()) {
                 return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
                         : STACK_INVISIBLE;
             }
-
-            // Otherwise, the docked stack is always visible, except in the case where the top
-            // running activity task in the focus stack doesn't support any form of resizing but we
-            // show it for the home task even though it's not resizable.
-            final TaskRecord task = r != null ? r.getTask() : null;
-            return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE
-                    : STACK_INVISIBLE;
+            return STACK_VISIBLE;
         }
 
         // Set home stack to invisible when it is below but not immediately below the docked stack
@@ -1692,14 +1684,17 @@
                 mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) {
             stackBehindTopIndex--;
         }
-        if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
-                && stackIndex == stackBehindTopIndex) {
-            // Stacks directly behind the docked or pinned stack are always visible.
-            return STACK_VISIBLE;
-        }
-
         final int stackBehindTopId = (stackBehindTopIndex >= 0)
                 ? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID;
+        if ((topStackId == DOCKED_STACK_ID || topStackId == PINNED_STACK_ID)
+                && (stackIndex == stackBehindTopIndex
+                || (stackBehindTopId == DOCKED_STACK_ID
+                && stackIndex == stackBehindTopIndex - 1))) {
+            // Stacks directly behind the docked or pinned stack are always visible.
+            // Also this stack is visible if behind docked stack and the docked stack is behind the
+            // top-most pinned stack
+            return STACK_VISIBLE;
+        }
 
         if (StackId.isBackdropToTranslucentActivity(topStackId)
                 && topStack.isStackTranslucent(starting, stackBehindTopId)) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 86a3103..1f1aa8e 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -575,12 +575,15 @@
             return;
         }
 
-        if (startedActivityStackId == PINNED_STACK_ID
-                && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP)) {
+        boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+        if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
+                || result == START_DELIVERED_TO_TOP || clearedTask)) {
             // The activity was already running in the pinned stack so it wasn't started, but either
             // brought to the front or the new intent was delivered to it since it was already in
             // front. Notify anyone interested in this piece of information.
-            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
+            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+                    clearedTask);
             return;
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b025385..fbc2bd2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -103,7 +103,6 @@
     int renderThreadTid;        // TID for RenderThread
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
-    boolean setIsForeground;    // Running foreground UI when last set?
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
     boolean hasClientActivities;  // Are there any client services with activities?
     boolean hasStartedServices; // Are there any started services running in this process?
@@ -303,9 +302,8 @@
                     pw.print(" hasAboveClient="); pw.print(hasAboveClient);
                     pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
         }
-        if (setIsForeground || foregroundServices || forcingToForeground != null) {
-            pw.print(prefix); pw.print("setIsForeground="); pw.print(setIsForeground);
-                    pw.print(" foregroundServices="); pw.print(foregroundServices);
+        if (foregroundServices || forcingToForeground != null) {
+            pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
                     pw.print(" forcingToForeground="); pw.println(forcingToForeground);
         }
         if (reportedInteraction || fgInteractionTime != 0) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 7d2bc5b..f5d7b68 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -104,7 +104,7 @@
     };
 
     private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
-        l.onPinnedActivityRestartAttempt();
+        l.onPinnedActivityRestartAttempt(m.arg1 != 0);
     };
 
     private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
@@ -300,10 +300,11 @@
      * running in the pinned stack and the activity was not actually started, but the task is
      * either brought to the front or a new Intent is delivered to it.
      */
-    void notifyPinnedActivityRestartAttempt() {
+    void notifyPinnedActivityRestartAttempt(boolean clearedTask) {
         mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
         final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
+                mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
+                        clearedTask ? 1 : 0, 0);
         forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
         msg.sendToTarget();
     }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c0fb77f..c411bce 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -35,6 +35,7 @@
     int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
     long lastBackgroundTime;
     boolean ephemeral;
+    boolean foregroundServices;
     boolean curWhitelist;
     boolean setWhitelist;
     boolean idle;
@@ -102,6 +103,7 @@
 
     public void reset() {
         curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        foregroundServices = false;
     }
 
     public void updateHasInternetPermission() {
@@ -131,6 +133,9 @@
         if (ephemeral) {
             sb.append(" ephemeral");
         }
+        if (foregroundServices) {
+            sb.append(" fgServices");
+        }
         if (curWhitelist) {
             sb.append(" whitelist");
         }
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d35104f..3053879 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -18,12 +18,10 @@
 
 import android.annotation.NonNull;
 import android.media.AudioAttributes;
-import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IPlaybackConfigDispatcher;
-import android.media.MediaRecorder;
 import android.media.PlayerBase;
 import android.media.VolumeShaper;
 import android.os.Binder;
@@ -99,13 +97,6 @@
         apc.init();
         synchronized(mPlayerLock) {
             mPlayers.put(newPiid, apc);
-            if (mDuckedUids.contains(new Integer(apc.getClientUid()))) {
-                if (DEBUG) { Log.v(TAG, "  > trackPlayer() piid=" + newPiid + " must be ducked"); }
-                mDuckedPlayers.add(new Integer(newPiid));
-                // FIXME here the player needs to be put in a state that is the same as if it
-                //   had been ducked as it starts. At the moment, this works already for linked
-                //   players, as is the case in gapless playback.
-            }
         }
         return newPiid;
     }
@@ -131,10 +122,11 @@
         final boolean change;
         synchronized(mPlayerLock) {
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
-            // FIXME SoundPool not ready for state reporting
-            if (apc != null
-                    && apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL)
-            {
+            if (apc == null) {
+                return;
+            }
+            if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
+                // FIXME SoundPool not ready for state reporting
                 return;
             }
             if (checkConfigurationCaller(piid, apc, binderUid)) {
@@ -144,12 +136,8 @@
                 Log.e(TAG, "Error handling event " + event);
                 change = false;
             }
-            if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
-                    && mDuckedUids.contains(new Integer(apc.getClientUid()))) {
-                if (DEBUG) { Log.v(TAG, "  > playerEvent() piid=" + piid + " must be ducked"); }
-                if (!mDuckedPlayers.contains(new Integer(piid))) {
-                    mDuckedPlayers.add(new Integer(piid));
-                }
+            if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+                mDuckingManager.checkDuck(apc);
             }
         }
         if (change) {
@@ -163,6 +151,7 @@
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
             if (checkConfigurationCaller(piid, apc, binderUid)) {
                 mPlayers.remove(new Integer(piid));
+                mDuckingManager.removeReleased(apc);
             }
         }
     }
@@ -182,10 +171,8 @@
                 conf.dump(pw);
             }
             // ducked players
-            pw.println("\n  ducked player piids:");
-            for (int piid : mDuckedPlayers) {
-                pw.println(" " + piid);
-            }
+            pw.println("\n  ducked players:");
+            mDuckingManager.dump(pw);
             // players muted due to the device ringing or being in a call
             pw.println("\n  muted player piids:");
             for (int piid : mMutedPlayers) {
@@ -274,10 +261,9 @@
 
     //=================================================================
     // PlayerFocusEnforcer implementation
-    private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
     private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
-    // size of 2 for typical cases of double-ducking, not expected to grow beyond that, but can
-    private final ArrayList<Integer> mDuckedUids = new ArrayList<Integer>(2);
+
+    private final DuckingManager mDuckingManager = new DuckingManager();
 
     @Override
     public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
@@ -286,60 +272,46 @@
                     winner.getClientUid(), loser.getClientUid()));
         }
         synchronized (mPlayerLock) {
-            final Integer loserUid = new Integer(loser.getClientUid());
-            if (!mDuckedUids.contains(loserUid)) {
-                mDuckedUids.add(loserUid);
-            }
             if (mPlayers.isEmpty()) {
                 return true;
             }
-            final Set<Integer> piidSet = mPlayers.keySet();
-            final Iterator<Integer> piidIterator = piidSet.iterator();
-            // find which players to duck
-            while (piidIterator.hasNext()) {
-                final Integer piid = piidIterator.next();
-                final AudioPlaybackConfiguration apc = mPlayers.get(piid);
-                if (apc == null) {
-                    continue;
-                }
+            // check if this UID needs to be ducked (return false if not), and gather list of
+            // eligible players to duck
+            final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
+            final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
+                    new ArrayList<AudioPlaybackConfiguration>();
+            while (apcIterator.hasNext()) {
+                final AudioPlaybackConfiguration apc = apcIterator.next();
                 if (!winner.hasSameUid(apc.getClientUid())
                         && loser.hasSameUid(apc.getClientUid())
                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
                 {
-                    if (mDuckedPlayers.contains(new Integer(piid))) {
-                        if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
-                    } else if (MediaFocusControl.ENFORCE_DUCKING
+                    if (MediaFocusControl.ENFORCE_DUCKING
                             && MediaFocusControl.ENFORCE_DUCKING_FOR_NEW
                             && loser.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL) {
                         // legacy behavior, apps used to be notified when they should be ducking
-                        if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": old SDK"); }
+                        if (DEBUG) {Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+                                + ": old SDK"); }
                         return false;
                     } else if (apc.getAudioAttributes().getContentType() ==
                             AudioAttributes.CONTENT_TYPE_SPEECH) {
                         // the player is speaking, ducking will make the speech unintelligible
                         // so let the app handle it instead
-                        if (DEBUG) { Log.v(TAG, "not ducking player " + piid + ": SPEECH"); }
+                        if (DEBUG) { Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
+                                + ": SPEECH"); }
                         return false;
                     } else if (apc.getPlayerType()
                             == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
                         // TODO support ducking of SoundPool players
                         return false;
-                    } else {
-                        try {
-                            Log.v(TAG, "ducking player " + piid);
-                            apc.getPlayerProxy().applyVolumeShaper(
-                                    DUCK_VSHAPE,
-                                    PLAY_CREATE_IF_NEEDED);
-                            mDuckedPlayers.add(new Integer(piid));
-                        } catch (Exception e) {
-                            Log.e(TAG, "Error ducking player " + piid, e);
-                            // something went wrong trying to duck, so let the app handle it
-                            // instead, it may know things we don't
-                            return false;
-                        }
                     }
+                    apcsToDuck.add(apc);
                 }
             }
+            // add the players eligible for ducking to the list, and duck them
+            // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
+            //  players of the same uid start, they will be ducked by DuckingManager.checkDuck())
+            mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
         }
         return true;
     }
@@ -348,37 +320,7 @@
     public void unduckPlayers(FocusRequester winner) {
         if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
         synchronized (mPlayerLock) {
-            if (mDuckedPlayers.isEmpty()) {
-                mDuckedUids.remove(new Integer(winner.getClientUid()));
-                return;
-            }
-            final ArrayList<Integer> playersToRemove =
-                    new ArrayList<Integer>(mDuckedPlayers.size());
-            for (int piid : mDuckedPlayers) {
-                final AudioPlaybackConfiguration apc = mPlayers.get(piid);
-                if (apc != null) {
-                    if (winner.hasSameUid(apc.getClientUid())) {
-                        try {
-                            Log.v(TAG, "unducking player " + piid);
-                            apc.getPlayerProxy().applyVolumeShaper(
-                                    DUCK_ID,
-                                    VolumeShaper.Operation.REVERSE);
-                        } catch (Exception e) {
-                            Log.e(TAG, "Error unducking player " + piid, e);
-                        } finally {
-                            playersToRemove.add(piid);
-                        }
-                    }
-                } else {
-                    // this piid was in the list of ducked players, but wasn't found, discard it
-                    Log.v(TAG, "Error unducking player " + piid + ", player not found");
-                    playersToRemove.add(piid);
-                }
-            }
-            for (int piid : playersToRemove) {
-                mDuckedPlayers.remove(new Integer(piid));
-            }
-            mDuckedUids.remove(new Integer(winner.getClientUid()));
+            mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
         }
     }
 
@@ -541,4 +483,118 @@
             mDispatcherCb.asBinder().unlinkToDeath(this, 0);
         }
     }
+
+    //=================================================================
+    // Class to handle ducking related operations for a given UID
+    private static final class DuckingManager {
+        private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
+
+        void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
+            if (!mDuckers.containsKey(uid)) {
+                mDuckers.put(uid, new DuckedApp(uid));
+            }
+            final DuckedApp da = mDuckers.get(uid);
+            for (AudioPlaybackConfiguration apc : apcsToDuck) {
+                da.addDuck(apc);
+            }
+        }
+
+        void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
+            final DuckedApp da = mDuckers.remove(uid);
+            if (da == null) {
+                return;
+            }
+            da.removeUnduckAll(players);
+        }
+
+        // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+        void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
+            final DuckedApp da = mDuckers.get(apc.getClientUid());
+            if (da == null) {
+                return;
+            }
+            // FIXME here the player needs to be put in a state that is the same as if it
+            //   had been ducked as it starts. At the moment, this works already for linked
+            //   players, as is the case in gapless playback.
+            da.addDuck(apc);
+        }
+
+        void dump(PrintWriter pw) {
+            for (DuckedApp da : mDuckers.values()) {
+                da.dump(pw);
+            }
+        }
+
+        void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+            final DuckedApp da = mDuckers.get(apc.getClientUid());
+            if (da == null) {
+                return;
+            }
+            da.removeReleased(apc);
+        }
+
+        private static final class DuckedApp {
+            private final int mUid;
+            private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
+
+            DuckedApp(int uid) {
+                mUid = uid;
+            }
+
+            void dump(PrintWriter pw) {
+                pw.print("\t uid:" + mUid + " piids:");
+                for (int piid : mDuckedPlayers) {
+                    pw.print(" " + piid);
+                }
+                pw.println("");
+            }
+
+            // pre-conditions:
+            //  * apc != null
+            //  * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
+            void addDuck(@NonNull AudioPlaybackConfiguration apc) {
+                final int piid = new Integer(apc.getPlayerInterfaceId());
+                if (mDuckedPlayers.contains(piid)) {
+                    if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
+                    return;
+                }
+                try {
+                    Log.v(TAG, "ducking player " + apc.getPlayerInterfaceId());
+                    apc.getPlayerProxy().applyVolumeShaper(
+                            DUCK_VSHAPE,
+                            PLAY_CREATE_IF_NEEDED);
+                    mDuckedPlayers.add(piid);
+                } catch (Exception e) {
+                    Log.e(TAG, "Error ducking player " + piid, e);
+                }
+            }
+
+            void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
+                for (int piid : mDuckedPlayers) {
+                    final AudioPlaybackConfiguration apc = players.get(piid);
+                    if (apc != null) {
+                        try {
+                            Log.v(TAG, "unducking player " + piid);
+                            apc.getPlayerProxy().applyVolumeShaper(
+                                    DUCK_ID,
+                                    VolumeShaper.Operation.REVERSE);
+                        } catch (Exception e) {
+                            Log.e(TAG, "Error unducking player " + piid, e);
+                        }
+                    } else {
+                        // this piid was in the list of ducked players, but wasn't found
+                        if (DEBUG) {
+                            Log.v(TAG, "Error unducking player " + piid + ", player not found for"
+                                    + " uid " + mUid);
+                        }
+                    }
+                }
+                mDuckedPlayers.clear();
+            }
+
+            void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
+                mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/fingerprint/RemovalClient.java b/services/core/java/com/android/server/fingerprint/RemovalClient.java
index 6610634..88a6bdd 100644
--- a/services/core/java/com/android/server/fingerprint/RemovalClient.java
+++ b/services/core/java/com/android/server/fingerprint/RemovalClient.java
@@ -91,7 +91,7 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to notify Removed:", e);
         }
-        return fingerId == 0;
+        return remaining == 0;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index f92bf3d..4981d5c 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -66,7 +66,11 @@
 
             @Override
             public void applyChangesLocked(NotificationRecord record) {
-                record.setRecentlyIntrusive(false);
+                // there will be another reconsideration in the message queue HANG_TIME_MS
+                // from each time this record alerts, which can finally clear this flag.
+                if ((System.currentTimeMillis() - record.getLastIntrusive()) >= HANG_TIME_MS) {
+                    record.setRecentlyIntrusive(false);
+                }
             }
         };
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48d11c3..1d843c1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -884,6 +884,8 @@
             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mZenModeHelper.onUserRemoved(user);
+                mRankingHelper.onUserRemoved(user);
+                savePolicyFile();
             } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mConditionProviders.onUserUnlocked(user);
@@ -3199,10 +3201,8 @@
                     + ", incomingUserId=" + incomingUserId
                     + ", notificationUid=" + notificationUid
                     + ", notification=" + notification;
-            // STOPSHIP TODO: should throw instead of logging or toasting.
-            // throw new IllegalArgumentException(noChannelStr);
             Log.e(TAG, noChannelStr);
-            doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
+            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
                     "Failed to post notification on channel \"" + channelId + "\"\n" +
                     "See log for more details");
             return;
@@ -3238,8 +3238,10 @@
         mHandler.post(new EnqueueNotificationRunnable(userId, r));
     }
 
-    private void doDebugOnlyToast(CharSequence toastText) {
-        if (Build.IS_DEBUGGABLE) {
+    private void doChannelWarningToast(CharSequence toastText) {
+        final boolean warningEnabled = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 0) != 0;
+        if (warningEnabled || Build.IS_DEBUGGABLE) {
             try {
                 Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
                 toast.show();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 90257da..f019a5c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -85,6 +85,7 @@
     // to communicate with the ranking module.
     private float mContactAffinity;
     private boolean mRecentlyIntrusive;
+    private long mLastIntrusive;
 
     // is this notification currently being intercepted by Zen Mode?
     private boolean mIntercept;
@@ -515,12 +516,19 @@
 
     public void setRecentlyIntrusive(boolean recentlyIntrusive) {
         mRecentlyIntrusive = recentlyIntrusive;
+        if (recentlyIntrusive) {
+            mLastIntrusive = System.currentTimeMillis();
+        }
     }
 
     public boolean isRecentlyIntrusive() {
         return mRecentlyIntrusive;
     }
 
+    public long getLastIntrusive() {
+        return mLastIntrusive;
+    }
+
     public void setPackagePriority(int packagePriority) {
         mPackagePriority = packagePriority;
     }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3481556..1e741de 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -1040,6 +1040,18 @@
         return packageChannels;
     }
 
+    public void onUserRemoved(int userId) {
+        synchronized (mRecords) {
+            int N = mRecords.size();
+            for (int i = N - 1; i >= 0 ; i--) {
+                Record record = mRecords.valueAt(i);
+                if (UserHandle.getUserId(record.uid) == userId) {
+                    mRecords.removeAt(i);
+                }
+            }
+        }
+    }
+
     public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
             int[] uidList) {
         if (pkgList == null || pkgList.length == 0) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index f94a56f..59bce8f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -634,9 +634,13 @@
 
         @Override
         protected int adjustDexoptNeeded(int dexoptNeeded) {
-            // Ensure compilation, no matter the current state.
-            // TODO: The return value is wrong when patchoat is needed.
-            return DexFile.DEX2OAT_FROM_SCRATCH;
+            if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
+                // Ensure compilation by pretending a compiler filter change on the
+                // apk/odex location (the reason for the '-'. A positive value means
+                // the 'oat' location).
+                return -DexFile.DEX2OAT_FOR_FILTER;
+            }
+            return dexoptNeeded;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cfcd0a5..121ae07 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18514,11 +18514,6 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
         synchronized (mPackages) {
-            PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName);
-                return false;
-            }
             // Cannot block uninstall of static shared libs as they are
             // considered a part of the using app (emulating static linking).
             // Also static libs are installed always on internal storage.
@@ -18528,12 +18523,7 @@
                         + " providing static shared library: " + pkg.staticSharedLibName);
                 return false;
             }
-            if (!ps.getInstalled(userId)) {
-                // Can't block uninstall for an app that is not installed or enabled.
-                Log.i(TAG, "Package not installed in set block uninstall " + packageName);
-                return false;
-            }
-            ps.setBlockUninstall(blockUninstall, userId);
+            mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
             mSettings.writePackageRestrictionsLPr(userId);
         }
         return true;
@@ -18542,12 +18532,7 @@
     @Override
     public boolean getBlockUninstallForUser(String packageName, int userId) {
         synchronized (mPackages) {
-            PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                Log.i(TAG, "Package doesn't exist in get block uninstall " + packageName);
-                return false;
-            }
-            return ps.getBlockUninstall(userId);
+            return mSettings.getBlockUninstallLPr(userId, packageName);
         }
     }
 
@@ -18754,7 +18739,6 @@
                     null /*lastDisableAppCaller*/,
                     null /*enabledComponents*/,
                     null /*disabledComponents*/,
-                    false /*blockUninstall*/,
                     ps.readUserState(nextUserId).domainVerificationStatus,
                     0, PackageManager.INSTALL_REASON_UNKNOWN);
         }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index dfed72f..14f65eb 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -402,14 +402,6 @@
         modifyUserState(userId).suspended = suspended;
     }
 
-    boolean getBlockUninstall(int userId) {
-        return readUserState(userId).blockUninstall;
-    }
-
-    void setBlockUninstall(boolean blockUninstall, int userId) {
-        modifyUserState(userId).blockUninstall = blockUninstall;
-    }
-
     boolean getInstantApp(int userId) {
         return readUserState(userId).instantApp;
     }
@@ -421,8 +413,8 @@
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
             String lastDisableAppCaller, ArraySet<String> enabledComponents,
-            ArraySet<String> disabledComponents, boolean blockUninstall,
-            int domainVerifState, int linkGeneration, int installReason) {
+            ArraySet<String> disabledComponents, int domainVerifState,
+            int linkGeneration, int installReason) {
         PackageUserState state = modifyUserState(userId);
         state.ceDataInode = ceDataInode;
         state.enabled = enabled;
@@ -434,7 +426,6 @@
         state.lastDisableAppCaller = lastDisableAppCaller;
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
-        state.blockUninstall = blockUninstall;
         state.domainVerificationStatus = domainVerifState;
         state.appLinkGeneration = linkGeneration;
         state.installReason = installReason;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index cea031e..44bcff2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -186,6 +186,8 @@
     private static final String TAG_PERMISSIONS = "perms";
     private static final String TAG_CHILD_PACKAGE = "child-package";
     private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
+    private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages";
+    private static final String TAG_BLOCK_UNINSTALL = "block-uninstall";
 
     private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
             "persistent-preferred-activities";
@@ -215,6 +217,8 @@
     // New name for the above attribute.
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_SUSPENDED = "suspended";
+    // Legacy, uninstall blocks are stored separately.
+    @Deprecated
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
     private static final String ATTR_ENABLED = "enabled";
     private static final String ATTR_ENABLED_CALLER = "enabledCaller";
@@ -271,6 +275,9 @@
     private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
         new ArrayMap<String, PackageSetting>();
 
+    /** List of packages that are blocked for uninstall for specific users */
+    private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>();
+
     // Set of restored intent-filter verification states
     private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
             new ArrayMap<String, IntentFilterVerificationInfo>();
@@ -756,7 +763,6 @@
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
-                                false /*blockUninstall*/,
                                 INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
                                 0, PackageManager.INSTALL_REASON_UNKNOWN);
                     }
@@ -1614,6 +1620,34 @@
         }
     }
 
+    void readBlockUninstallPackagesLPw(XmlPullParser parser, int userId)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        ArraySet<String> packages = new ArraySet<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_BLOCK_UNINSTALL)) {
+                String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+                packages.add(packageName);
+            } else {
+                String msg = "Unknown element under " +  TAG_BLOCK_UNINSTALL_PACKAGES + ": " +
+                        parser.getName();
+                PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        if (packages.isEmpty()) {
+            mBlockUninstallPackages.remove(userId);
+        } else {
+            mBlockUninstallPackages.put(userId, packages);
+        }
+    }
+
     void readPackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1662,7 +1696,6 @@
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
-                                false /*blockUninstall*/,
                                 INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
                                 0, PackageManager.INSTALL_REASON_UNKNOWN);
                     }
@@ -1768,9 +1801,12 @@
                         }
                     }
 
+                    if (blockUninstall) {
+                        setBlockUninstallLPw(userId, name, true);
+                    }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
                             hidden, suspended, instantApp, enabledCaller, enabledComponents,
-                            disabledComponents, blockUninstall, verifState, linkGeneration,
+                            disabledComponents, verifState, linkGeneration,
                             installReason);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
@@ -1780,6 +1816,8 @@
                     readCrossProfileIntentFiltersLPw(parser, userId);
                 } else if (tagName.equals(TAG_DEFAULT_APPS)) {
                     readDefaultAppsLPw(parser, userId);
+                } else if (tagName.equals(TAG_BLOCK_UNINSTALL_PACKAGES)) {
+                    readBlockUninstallPackagesLPw(parser, userId);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
                           + parser.getName());
@@ -1806,6 +1844,30 @@
         }
     }
 
+    void setBlockUninstallLPw(int userId, String packageName, boolean blockUninstall) {
+        ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+        if (blockUninstall) {
+            if (packages == null) {
+                packages = new ArraySet<String>();
+                mBlockUninstallPackages.put(userId, packages);
+            }
+            packages.add(packageName);
+        } else if (packages != null) {
+            packages.remove(packageName);
+            if (packages.isEmpty()) {
+                mBlockUninstallPackages.remove(userId);
+            }
+        }
+    }
+
+    boolean getBlockUninstallLPr(int userId, String packageName) {
+        ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+        if (packages == null) {
+            return false;
+        }
+        return packages.contains(packageName);
+    }
+
     private ArraySet<String> readComponentsLPr(XmlPullParser parser)
             throws IOException, XmlPullParserException {
         ArraySet<String> components = null;
@@ -1976,6 +2038,20 @@
         serializer.endTag(null, TAG_DEFAULT_APPS);
     }
 
+    void writeBlockUninstallPackagesLPr(XmlSerializer serializer, int userId)
+            throws IOException  {
+        ArraySet<String> packages = mBlockUninstallPackages.get(userId);
+        if (packages != null) {
+            serializer.startTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+            for (int i = 0; i < packages.size(); i++) {
+                 serializer.startTag(null, TAG_BLOCK_UNINSTALL);
+                 serializer.attribute(null, ATTR_PACKAGE_NAME, packages.valueAt(i));
+                 serializer.endTag(null, TAG_BLOCK_UNINSTALL);
+            }
+            serializer.endTag(null, TAG_BLOCK_UNINSTALL_PACKAGES);
+        }
+    }
+
     void writePackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -2038,9 +2114,6 @@
                 if (ustate.suspended) {
                     serializer.attribute(null, ATTR_SUSPENDED, "true");
                 }
-                if (ustate.blockUninstall) {
-                    serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true");
-                }
                 if (ustate.instantApp) {
                     serializer.attribute(null, ATTR_INSTANT_APP, "true");
                 }
@@ -2091,6 +2164,7 @@
             writePersistentPreferredActivitiesLPr(serializer, userId);
             writeCrossProfileIntentFiltersLPr(serializer, userId);
             writeDefaultAppsLPr(serializer, userId);
+            writeBlockUninstallPackagesLPr(serializer, userId);
 
             serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f4f7e24..8e06a51 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -325,8 +325,6 @@
                     agentInfo.label = resolveInfo.loadLabel(pm);
                     agentInfo.icon = resolveInfo.loadIcon(pm);
                     agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
-                    agentInfo.agent = new TrustAgentWrapper(mContext, this,
-                            new Intent().setComponent(name), userInfo.getUserHandle());
                 } else {
                     int index = mActiveAgents.indexOf(agentInfo);
                     agentInfo = mActiveAgents.valueAt(index);
@@ -363,6 +361,11 @@
                     }
                 }
 
+                if (agentInfo.agent == null) {
+                    agentInfo.agent = new TrustAgentWrapper(mContext, this,
+                            new Intent().setComponent(name), userInfo.getUserHandle());
+                }
+
                 if (!mActiveAgents.contains(agentInfo)) {
                     mActiveAgents.add(agentInfo);
                 } else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index be242b6..b27d6c4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2891,21 +2891,6 @@
         final Rect frame = new Rect();
         final Rect stackBounds = new Rect();
 
-        boolean includeImeInScreenshot;
-        synchronized(mService.mWindowMap) {
-            final AppWindowToken imeTargetAppToken = mService.mInputMethodTarget != null
-                    ? mService.mInputMethodTarget.mAppToken : null;
-            // We only include the Ime in the screenshot if the app we are screenshoting is the IME
-            // target and isn't in multi-window mode. We don't screenshot the IME in multi-window
-            // mode because the frame of the IME might not overlap with that of the app.
-            // E.g. IME target app at the top in split-screen mode and the IME at the bottom
-            // overlapping with the bottom app.
-            includeImeInScreenshot = imeTargetAppToken != null
-                    && imeTargetAppToken.appToken != null
-                    && imeTargetAppToken.appToken.asBinder() == appToken
-                    && !mService.mInputMethodTarget.isInMultiWindowMode();
-        }
-
         final int aboveAppLayer = (mService.mPolicy.getWindowLayerFromTypeLw(TYPE_APPLICATION) + 1)
                 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
         final MutableBoolean mutableIncludeFullDisplay = new MutableBoolean(includeFullDisplay);
@@ -2923,9 +2908,7 @@
                     return false;
                 }
                 if (w.mIsImWindow) {
-                    if (!includeImeInScreenshot) {
-                        return false;
-                    }
+                    return false;
                 } else if (w.mIsWallpaper) {
                     // If this is the wallpaper layer and we're only looking for the wallpaper layer
                     // then the target window state is this one.
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index e2428b9..8ab6d08 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -843,6 +843,36 @@
     }
 
     @Test
+    public void testOnUserRemoved() throws Exception {
+        int[] user0Uids = {98, 235, 16, 3782};
+        int[] user1Uids = new int[user0Uids.length];
+        for (int i = 0; i < user0Uids.length; i++) {
+            user1Uids[i] = UserHandle.PER_USER_RANGE + user0Uids[i];
+
+            final ApplicationInfo legacy = new ApplicationInfo();
+            legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+            when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
+
+            // create records with the default channel for all user 0 and user 1 uids
+            mHelper.getImportance(PKG, user0Uids[i]);
+            mHelper.getImportance(PKG, user1Uids[i]);
+        }
+
+        mHelper.onUserRemoved(1);
+
+        // user 0 records remain
+        for (int i = 0; i < user0Uids.length; i++) {
+            assertEquals(1,
+                    mHelper.getNotificationChannels(PKG, user0Uids[i], false).getList().size());
+        }
+        // user 1 records are gone
+        for (int i = 0; i < user1Uids.length; i++) {
+            assertEquals(0,
+                    mHelper.getNotificationChannels(PKG, user1Uids[i], false).getList().size());
+        }
+    }
+
+    @Test
     public void testOnPackageChanged_packageRemoval() throws Exception {
         // Deleted
         NotificationChannel channel1 =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 325d99a..5e4ba7be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -31,13 +31,10 @@
 import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.UserInfo;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.support.test.InstrumentationRegistry;
@@ -49,26 +46,17 @@
 import android.util.LongSparseArray;
 
 import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastPrintWriter;
 import com.android.server.LocalServices;
 
-import org.hamcrest.core.IsNot;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.nio.charset.StandardCharsets;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.PrintStream;
 import java.security.PublicKey;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
@@ -547,7 +535,6 @@
 
     private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
             boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
-        assertThat(userState.blockUninstall, is(false));
         assertThat(userState.enabled, is(0));
         assertThat(userState.hidden, is(false));
         assertThat(userState.installed, is(installed));
@@ -783,31 +770,14 @@
 
     @Before
     public void createUserManagerServiceRef() throws ReflectiveOperationException {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                Constructor<UserManagerService> umsc;
-                try {
-                    // unregister the user manager from the local service
-                    Method removeServiceForTest = LocalServices.class.getDeclaredMethod(
-                            "removeServiceForTest", Class.class);
-                    removeServiceForTest.invoke(null, UserManagerInternal.class);
-
-                    // now create a new user manager [which registers again with the local service]
-                    umsc = UserManagerService.class.getDeclaredConstructor(
-                            Context.class,
-                            PackageManagerService.class,
-                            Object.class,
-                            File.class);
-                    umsc.setAccessible(true);
-                    UserManagerService ums = umsc.newInstance(InstrumentationRegistry.getContext(),
-                            null /*PackageManagerService*/, new Object() /*packagesLock*/,
-                            new File(InstrumentationRegistry.getContext().getFilesDir(), "user"));
-                } catch (SecurityException
-                        | ReflectiveOperationException
-                        | IllegalArgumentException e) {
-                    fail("Could not create user manager service; " + e);
-                }
+        InstrumentationRegistry.getInstrumentation().runOnMainSync((Runnable) () -> {
+            try {
+                // unregister the user manager from the local service
+                LocalServices.removeServiceForTest(UserManagerInternal.class);
+                new UserManagerService(InstrumentationRegistry.getContext());
+            } catch (Exception e) {
+                e.printStackTrace();
+                fail("Could not create user manager service; " + e);
             }
         });
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index bf05f21..50be8db 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -49,10 +49,6 @@
         assertThat(testUserState.equals(oldUserState), is(false));
 
         oldUserState = new PackageUserState();
-        oldUserState.blockUninstall = true;
-        assertThat(testUserState.equals(oldUserState), is(false));
-
-        oldUserState = new PackageUserState();
         oldUserState.ceDataInode = 4000L;
         assertThat(testUserState.equals(oldUserState), is(false));
 
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
index 38ea6e6f..a75cd86 100644
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java
@@ -120,7 +120,7 @@
     public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
                     incomingCallIntent, listener);
         }
@@ -129,7 +129,7 @@
     @Override
     public void endSession(int sessionId) throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
         }
     }
@@ -138,7 +138,7 @@
     public boolean isConnected(int callServiceType, int callType)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
                     callServiceType, callType);
         }
@@ -147,7 +147,7 @@
     @Override
     public boolean isOpened() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
         }
     }
@@ -156,7 +156,7 @@
     public void addRegistrationListener(IImsRegistrationListener listener)
     throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
                     listener);
         }
@@ -166,7 +166,7 @@
     public void removeRegistrationListener(IImsRegistrationListener listener)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
                     listener);
         }
@@ -176,7 +176,7 @@
     public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
                     sessionId, callServiceType, callType);
         }
@@ -186,7 +186,7 @@
     public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
             IImsCallSessionListener listener) throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
                     sessionId, profile, listener);
         }
@@ -196,7 +196,7 @@
     public IImsCallSession getPendingCallSession(int sessionId, String callId)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
                     sessionId, callId);
         }
@@ -205,7 +205,7 @@
     @Override
     public IImsUt getUtInterface() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
         }
     }
@@ -213,7 +213,7 @@
     @Override
     public IImsConfig getConfigInterface() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
         }
     }
@@ -221,7 +221,7 @@
     @Override
     public void turnOnIms() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
         }
     }
@@ -229,7 +229,7 @@
     @Override
     public void turnOffIms() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
         }
     }
@@ -237,7 +237,7 @@
     @Override
     public IImsEcbm getEcbmInterface() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
         }
     }
@@ -246,7 +246,7 @@
     public void setUiTTYMode(int uiTtyMode, Message onComplete)
             throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
                     onComplete);
         }
@@ -255,7 +255,7 @@
     @Override
     public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
         synchronized (mLock) {
-            checkBinderConnection();
+            checkServiceIsReady();
             return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
                     mSupportedFeature);
         }
@@ -264,7 +264,8 @@
     @Override
     public int getFeatureStatus() {
         synchronized (mLock) {
-            if (mFeatureStatusCached != null) {
+            if (isBinderAlive() && mFeatureStatusCached != null) {
+                Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
                 return mFeatureStatusCached;
             }
         }
@@ -277,6 +278,7 @@
             // Cache only non-null value for feature status.
             mFeatureStatusCached = status;
         }
+        Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
         return status;
     }
 
@@ -301,10 +303,28 @@
         mStatusCallback = c;
     }
 
+    /**
+     * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
+     * method returns false, it doesn't mean that the Binder connection is not available (use
+     * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
+     * at this time.
+     *
+     * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
+     * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
+     */
+    public boolean isBinderReady() {
+        return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
+    }
+
     @Override
     public boolean isBinderAlive() {
-        return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null &&
-                mBinder.isBinderAlive();
+        return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
+    }
+
+    protected void checkServiceIsReady() throws RemoteException {
+        if (!isBinderReady()) {
+            throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
+        }
     }
 
     private IImsServiceController getServiceInterface(IBinder b) {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 988dd58..395f1cc 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -150,7 +150,7 @@
     private void notifyFeatureState(@ImsState int state) {
         if (mStatusCallback != null) {
             try {
-                Log.i(LOG_TAG, "notifying ImsFeatureState");
+                Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
                 mStatusCallback.notifyImsFeatureStatus(state);
             } catch (RemoteException e) {
                 mStatusCallback = null;