Merge "Implemented WebView.onProvideContentCaptureStructure()"
diff --git a/api/current.txt b/api/current.txt
index 28863b8..ea6f190 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -9475,6 +9475,7 @@
 
   public abstract class Context {
     ctor public Context();
+    method public abstract boolean bindIsolatedService(android.content.Intent, android.content.ServiceConnection, int, java.lang.String);
     method public abstract boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public abstract int checkCallingOrSelfPermission(java.lang.String);
     method public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
@@ -9686,6 +9687,7 @@
   public class ContextWrapper extends android.content.Context {
     ctor public ContextWrapper(android.content.Context);
     method protected void attachBaseContext(android.content.Context);
+    method public boolean bindIsolatedService(android.content.Intent, android.content.ServiceConnection, int, java.lang.String);
     method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public int checkCallingOrSelfPermission(java.lang.String);
     method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
@@ -43682,6 +43684,7 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
+    method public int getPreferredOpportunisticDataSubscription();
     method public android.telephony.ServiceState getServiceState();
     method public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
@@ -43731,6 +43734,7 @@
     method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
+    method public boolean setPreferredOpportunisticDataSubscription(int);
     method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -54345,6 +54349,7 @@
     method public int getSourceWidth();
     method public int getWidth();
     method public float getZoom();
+    method public boolean isForcePositionWithinWindowSystemInsetsBounds();
     method public void setZoom(float);
     method public void show(float, float);
     method public void show(float, float, float, float);
@@ -54357,6 +54362,7 @@
     method public android.widget.Magnifier.Builder setCornerRadius(float);
     method public android.widget.Magnifier.Builder setDefaultSourceToMagnifierOffset(int, int);
     method public android.widget.Magnifier.Builder setElevation(float);
+    method public android.widget.Magnifier.Builder setForcePositionWithinWindowSystemInsetsBounds(boolean);
     method public android.widget.Magnifier.Builder setSize(int, int);
     method public android.widget.Magnifier.Builder setZoom(float);
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 21e7203..53d9673 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -147,6 +147,7 @@
         PhoneStateChanged phone_state_changed = 95;
         UserRestrictionChanged user_restriction_changed = 96;
         SettingsUIChanged settings_ui_changed = 97;
+        ConnectivityStateChanged connectivity_state_changed = 98;
     }
 
     // Pulled events will start at field 10000.
@@ -2129,6 +2130,22 @@
     optional int64 visible_millis = 16;
 }
 
+/*
+ * Logs when a connection becomes available and lost.
+ * Logged in StatsCompanionService.java
+ */
+message ConnectivityStateChanged {
+    // Id of the network.
+    optional int32 net_id = 1;
+
+    enum State {
+        UNKNOWN = 0;
+        CONNECTED = 1;
+        DISCONNECTED = 2;
+    }
+    // Connected state of a network.
+    optional State state = 2;
+}
 
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 05103a9..461ad28 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -140,7 +140,7 @@
 
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
-    if (mIsPulled) {
+    if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         pullAndMatchEventsLocked(startTimeNs);
     }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 5866139..6e3530b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -80,7 +80,7 @@
         }
         flushCurrentBucketLocked(eventTimeNs);
         mCurrentBucketStartTimeNs = eventTimeNs;
-        if (mIsPulled) {
+        if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
             pullAndMatchEventsLocked(eventTimeNs);
         }
     };
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index dc707e8..9837deb 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -140,6 +140,13 @@
         throw new ReceiverCallNotAllowedException(
                 "BroadcastReceiver components are not allowed to bind to services");
     }
+
+    @Override
+    public boolean bindIsolatedService(Intent service, ServiceConnection conn, int flags,
+            String instanceName) {
+        throw new ReceiverCallNotAllowedException(
+                "BroadcastReceiver components are not allowed to bind to services");
+    }
 }
 
 /**
@@ -1630,14 +1637,25 @@
     public boolean bindService(Intent service, ServiceConnection conn,
             int flags) {
         warnIfCallingFromSystemProcess();
-        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
+        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), getUser());
+    }
+
+    @Override
+    public boolean bindIsolatedService(Intent service, ServiceConnection conn,
+            int flags, String instanceName) {
+        warnIfCallingFromSystemProcess();
+        if (instanceName == null) {
+            throw new NullPointerException("null instanceName");
+        }
+        return bindServiceCommon(service, conn, flags, instanceName, mMainThread.getHandler(),
+                getUser());
     }
 
     /** @hide */
     @Override
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             UserHandle user) {
-        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);
+        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), user);
     }
 
     /** @hide */
@@ -1647,7 +1665,7 @@
         if (handler == null) {
             throw new IllegalArgumentException("handler must not be null.");
         }
-        return bindServiceCommon(service, conn, flags, handler, user);
+        return bindServiceCommon(service, conn, flags, null, handler, user);
     }
 
     /** @hide */
@@ -1669,7 +1687,8 @@
         return mMainThread.getHandler();
     }
 
-    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
+    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
+            String instanceName, Handler
             handler, UserHandle user) {
         // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
         IServiceConnection sd;
@@ -1690,10 +1709,10 @@
                 flags |= BIND_WAIVE_PRIORITY;
             }
             service.prepareToLeaveProcess(this);
-            int res = ActivityManager.getService().bindService(
+            int res = ActivityManager.getService().bindIsolatedService(
                 mMainThread.getApplicationThread(), getActivityToken(), service,
                 service.resolveTypeIfNeeded(getContentResolver()),
-                sd, flags, getOpPackageName(), user.getIdentifier());
+                sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
             if (res < 0) {
                 throw new SecurityException(
                         "Not allowed to bind to service " + service);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e2312a5..f27c667 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -133,9 +133,13 @@
             in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
+    // Currently keeping old bindService because it is on the greylist
     int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
             in String resolvedType, in IServiceConnection connection, int flags,
             in String callingPackage, int userId);
+    int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
+            in String resolvedType, in IServiceConnection connection, int flags,
+            in String instanceName, in String callingPackage, int userId);
     boolean unbindService(in IServiceConnection connection);
     void publishService(in IBinder token, in Intent intent, in IBinder service);
     void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9f8ae0b..03eba7e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2906,8 +2906,9 @@
      * @param flags Operation options for the binding.  May be 0,
      *          {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
      *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
-     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
-     *          {@link #BIND_WAIVE_PRIORITY}.
+     *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
+     *          {@link #BIND_IMPORTANT}, or
+     *          {@link #BIND_ADJUST_WITH_ACTIVITY}.
      * @return {@code true} if the system is in the process of bringing up a
      *         service that your client has permission to bind to; {@code false}
      *         if the system couldn't find the service or if your client doesn't
@@ -2923,11 +2924,38 @@
      * @see #BIND_AUTO_CREATE
      * @see #BIND_DEBUG_UNBIND
      * @see #BIND_NOT_FOREGROUND
+     * @see #BIND_ABOVE_CLIENT
+     * @see #BIND_ALLOW_OOM_MANAGEMENT
+     * @see #BIND_WAIVE_PRIORITY
+     * @see #BIND_IMPORTANT
+     * @see #BIND_ADJUST_WITH_ACTIVITY
      */
     public abstract boolean bindService(@RequiresPermission Intent service,
             @NonNull ServiceConnection conn, @BindServiceFlags int flags);
 
     /**
+     * Variation of {@link #bindService} that, in the specific case of isolated
+     * services, allows the caller to generate multiple instances of a service
+     * from a single component declaration.
+     *
+     * @param service Identifies the service to connect to.  The Intent must
+     *      specify an explicit component name.
+     * @param conn Receives information as the service is started and stopped.
+     *      This must be a valid ServiceConnection object; it must not be null.
+     * @param flags Operation options for the binding as per {@link #bindService}.
+     * @param instanceName Unique identifier for the service instance.  Each unique
+     *      name here will result in a different service instance being created.
+     * @return Returns success of binding as per {@link #bindService}.
+     *
+     * @throws SecurityException If the caller does not have permission to access the service
+     *
+     * @see #bindService
+     */
+    public abstract boolean bindIsolatedService(@RequiresPermission Intent service,
+            @NonNull ServiceConnection conn, @BindServiceFlags int flags,
+            @NonNull String instanceName);
+
+    /**
      * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle
      * argument for use by system server and other multi-user aware code.
      * @hide
@@ -2941,7 +2969,7 @@
     }
 
     /**
-     * Same as {@link #bindService(Intent, ServiceConnection, int, UserHandle)}, but with an
+     * Same as {@link #bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)}, but with an
      * explicit non-null Handler to run the ServiceConnection callbacks on.
      *
      * @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index bfad2b4..88696b0 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -705,6 +705,12 @@
         return mBase.bindService(service, conn, flags);
     }
 
+    @Override
+    public boolean bindIsolatedService(Intent service, ServiceConnection conn,
+            int flags, String instanceName) {
+        return mBase.bindIsolatedService(service, conn, flags, instanceName);
+    }
+
     /** @hide */
     @Override
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d679fc7..423ce77 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -268,6 +268,9 @@
     public static final int ENCRYPTION_STATE_ERROR_CORRUPT =
             IVold.ENCRYPTION_STATE_ERROR_CORRUPT;
 
+    /** @hide Prefix used in sandboxIds for apps with sharedUserIds */
+    public static final String SHARED_SANDBOX_PREFIX = "shared-";
+
     private static volatile IStorageManager sStorageManager = null;
 
     private final Context mContext;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0d33bbd..3d16eb8 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -572,6 +572,10 @@
      * Good practice is to first create the surface with the {@link #HIDDEN} flag
      * specified, open a transaction, set the surface layer, layer stack, alpha,
      * and position, call {@link #show} if appropriate, and close the transaction.
+     * <p>
+     * Bounds of the surface is determined by its crop and its buffer size. If the
+     * surface has no buffer or crop, the surface is boundless and only constrained
+     * by the size of its parent bounds.
      *
      * @param session The surface session, must not be null.
      * @param name The surface name, must not be null.
@@ -959,6 +963,14 @@
         }
     }
 
+    /**
+     * Bounds the surface and its children to the bounds specified. Size of the surface will be
+     * ignored and only the crop and buffer size will be used to determine the bounds of the
+     * surface. If no crop is specified and the surface has no buffer, the surface bounds is only
+     * constrained by the size of its parent bounds.
+     *
+     * @param crop Bounds of the crop to apply.
+     */
     public void setWindowCrop(Rect crop) {
         checkNotReleased();
         synchronized (SurfaceControl.class) {
@@ -966,6 +978,19 @@
         }
     }
 
+    /**
+     * Same as {@link SurfaceControl#setWindowCrop(Rect)} but sets the crop rect top left at 0, 0.
+     *
+     * @param width width of crop rect
+     * @param height height of crop rect
+     */
+    public void setWindowCrop(int width, int height) {
+        checkNotReleased();
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setWindowCrop(this, width, height);
+        }
+    }
+
     public void setLayerStack(int layerStack) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -1477,6 +1502,12 @@
             return this;
         }
 
+        public Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
+            sc.checkNotReleased();
+            nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, width, height);
+            return this;
+        }
+
         @UnsupportedAppUsage
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             sc.checkNotReleased();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 67f9399..2b68ec0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -598,6 +598,7 @@
                         }
                         if (sizeChanged && !creating) {
                             mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
+                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                         }
                     } finally {
                         SurfaceControl.closeTransaction();
@@ -1169,6 +1170,12 @@
         }
 
         @Override
+        public void setWindowCrop(int width, int height) {
+            super.setWindowCrop(width, height);
+            mBackgroundControl.setWindowCrop(width, height);
+        }
+
+        @Override
         public void setLayerStack(int layerStack) {
             super.setLayerStack(layerStack);
             mBackgroundControl.setLayerStack(layerStack);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 4a7e783..a8debbd 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -77,11 +77,17 @@
         CONSUMED = new WindowInsets((Insets) null, null, null, false, false, null);
     }
 
-    /** @hide */
+    /**
+     * Construct a new WindowInsets from individual insets.
+     *
+     * A {@code null} inset indicates that the respective inset is consumed.
+     *
+     * @hide
+     */
     public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
             boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
-        this(Insets.of(systemWindowInsets), Insets.of(windowDecorInsets), Insets.of(stableInsets),
-                isRound, alwaysConsumeNavBar, displayCutout);
+        this(insetsOrNull(systemWindowInsets), insetsOrNull(windowDecorInsets),
+                insetsOrNull(stableInsets), isRound, alwaysConsumeNavBar, displayCutout);
     }
 
     private WindowInsets(Insets systemWindowInsets, Insets windowDecorInsets,
@@ -673,6 +679,10 @@
         return Insets.of(newLeft, newTop, newRight, newBottom);
     }
 
+    private static Insets insetsOrNull(Rect insets) {
+        return insets != null ? Insets.of(insets) : null;
+    }
+
     /**
      * @return whether system window insets have been consumed.
      */
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 9da2a43..9f509b1 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -93,6 +93,8 @@
     private final int mDefaultHorizontalSourceToMagnifierOffset;
     // The vertical offset between the source and window coords when #show(float, float) is used.
     private final int mDefaultVerticalSourceToMagnifierOffset;
+    // Whether the magnifier will be clamped inside the main surface and not overlap system insets.
+    private final boolean mForcePositionWithinWindowSystemInsetsBounds;
     // The parent surface for the magnifier surface.
     private SurfaceInfo mParentSurface;
     // The surface where the content will be copied from.
@@ -141,6 +143,8 @@
                 params.mHorizontalDefaultSourceToMagnifierOffset;
         mDefaultVerticalSourceToMagnifierOffset =
                 params.mVerticalDefaultSourceToMagnifierOffset;
+        mForcePositionWithinWindowSystemInsetsBounds =
+                params.mForcePositionWithinWindowSystemInsetsBounds;
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
     }
@@ -379,6 +383,17 @@
     }
 
     /**
+     * Returns whether the magnifier position will be adjusted such that the magnifier will be
+     * fully within the bounds of the main application window, by also avoiding any overlap with
+     * system insets (such as the one corresponding to the status bar).
+     * @return whether the magnifier position will be adjusted
+     * @see Magnifier.Builder#setForcePositionWithinWindowSystemInsetsBounds(boolean)
+     */
+    public boolean isForcePositionWithinWindowSystemInsetsBounds() {
+        return mForcePositionWithinWindowSystemInsetsBounds;
+    }
+
+    /**
      * Returns the top left coordinates of the magnifier, relative to the surface of the
      * main application window. They will be determined by the coordinates of the last
      * {@link #show(float, float)} or {@link #show(float, float, float, float)} call, adjusted
@@ -567,6 +582,11 @@
      * @return the current window coordinates, after they are clamped inside the parent surface
      */
     private Point getCurrentClampedWindowCoordinates() {
+        if (!mForcePositionWithinWindowSystemInsetsBounds) {
+            // No position adjustment should be done, so return the raw coordinates.
+            return new Point(mWindowCoords);
+        }
+
         final Rect windowBounds;
         if (mParentSurface.mIsMainWindowSurface) {
             final Insets systemInsets = mView.getRootWindowInsets().getSystemWindowInsets();
@@ -891,6 +911,7 @@
         private @FloatRange(from = 0f) float mCornerRadius;
         private int mHorizontalDefaultSourceToMagnifierOffset;
         private int mVerticalDefaultSourceToMagnifierOffset;
+        private boolean mForcePositionWithinWindowSystemInsetsBounds;
 
         /**
          * Construct a new builder for {@link Magnifier} objects.
@@ -915,6 +936,7 @@
             mVerticalDefaultSourceToMagnifierOffset =
                     a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
             a.recycle();
+            mForcePositionWithinWindowSystemInsetsBounds = true;
         }
 
         /**
@@ -1000,6 +1022,28 @@
         }
 
         /**
+         * Defines the behavior of the magnifier when it is requested to position outside the
+         * surface of the main application window. The default value is {@code true}, which means
+         * that the position will be adjusted such that the magnifier will be fully within the
+         * bounds of the main application window, by also avoiding any overlap with system insets
+         * (such as the one corresponding to the status bar). If you require a custom behavior, this
+         * flag should be set to {@code false}, meaning that the magnifier will be able to cross the
+         * main application surface boundaries (and also overlap the system insets). This should be
+         * handled with care, when passing coordinates to {@link #show(float, float)}; note that:
+         * <ul>
+         *   <li>in a multiwindow context, if the magnifier crosses the boundary between the two
+         *   windows, it will not be able to show over the window of the other application</li>
+         *   <li>if the magnifier overlaps the status bar, there is no guarantee about which one
+         *   will be displayed on top. This should be handled with care.</li>
+         * </ul>
+         * @param force whether the magnifier position will be adjusted
+         */
+        public Builder setForcePositionWithinWindowSystemInsetsBounds(boolean force) {
+            mForcePositionWithinWindowSystemInsetsBounds = force;
+            return this;
+        }
+
+        /**
          * Builds a {@link Magnifier} instance based on the configuration of this {@link Builder}.
          */
         public @NonNull Magnifier build() {
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
new file mode 100644
index 0000000..1c2df2c
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowInsetsTest {
+
+    @Test
+    public void systemWindowInsets_afterConsuming_isConsumed() {
+        assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, null, false, false, null)
+                .consumeSystemWindowInsets().isConsumed());
+    }
+
+    @Test
+    public void multiNullConstructor_isConsumed() {
+        assertTrue(new WindowInsets(null, null, null, false, false, null).isConsumed());
+    }
+
+    @Test
+    public void singleNullConstructor_isConsumed() {
+        assertTrue(new WindowInsets((Rect) null).isConsumed());
+    }
+
+}
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 01382aa..4e0f2a8 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -81,7 +81,7 @@
 
     @Test
     public void testSharedInstalledPrimary() throws Exception {
-        assertEquals("1001", getContent(getKernelPackageFile("shared:android.uid.phone", "appid")));
+        assertEquals("1001", getContent(getKernelPackageFile("shared-android.uid.phone", "appid")));
     }
 
     @Test
@@ -92,7 +92,7 @@
 
     @Test
     public void testSharedInstalledAll() throws Exception {
-        assertEquals("", getContent(getKernelPackageFile("shared:android.uid.phone",
+        assertEquals("", getContent(getKernelPackageFile("shared-android.uid.phone",
                 "excluded_userids")));
     }
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c7945bd02..84cb5f8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -194,6 +194,8 @@
 
     <privapp-permissions package="com.android.providers.calendar">
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS" />
+        <permission name="android.permission.MANAGE_USERS" />
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
     </privapp-permissions>
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 3534636..9b97b10 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -56,15 +56,20 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.UUID;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * @hide
@@ -94,17 +99,11 @@
 
     private final Object mSrcLock = new Object();
     //--- guarded by |mSrcLock| start
-    private long mSrcIdGenerator = 0;
-    private DataSourceDesc mCurrentDSD;
-    private long mCurrentSrcId = mSrcIdGenerator++;
-    private List<DataSourceDesc> mNextDSDs;
-    private long mNextSrcId = mSrcIdGenerator++;
-    private int mNextSourceState = NEXT_SOURCE_STATE_INIT;
-    private boolean mNextSourcePlayPending = false;
+    private SourceInfo mCurrentSourceInfo;
+    private final Queue<SourceInfo> mNextSourceInfos = new ConcurrentLinkedQueue<>();
     //--- guarded by |mSrcLock| end
+    private final AtomicLong mSrcIdGenerator = new AtomicLong(0);
 
-    private AtomicInteger mBufferedPercentageCurrent = new AtomicInteger(0);
-    private AtomicInteger mBufferedPercentageNext = new AtomicInteger(0);
     private volatile float mVolume = 1.0f;
     private VideoSize mVideoSize = new VideoSize(0, 0);
 
@@ -227,7 +226,15 @@
     @Override
     public long getBufferedPosition() {
         // Use cached buffered percent for now.
-        return getDuration() * mBufferedPercentageCurrent.get() / 100;
+        int bufferedPercentage;
+        synchronized (mSrcLock) {
+            if (mCurrentSourceInfo == null) {
+                bufferedPercentage = 0;
+            } else {
+                bufferedPercentage = mCurrentSourceInfo.mBufferedPercentage.get();
+            }
+        }
+        return getDuration() * bufferedPercentage / 100;
     }
 
     @Override
@@ -268,9 +275,8 @@
                 }
 
                 synchronized (mSrcLock) {
-                    mCurrentDSD = dsd;
-                    mCurrentSrcId = mSrcIdGenerator++;
-                    handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
+                    mCurrentSourceInfo = new SourceInfo(dsd);
+                    handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
                 }
             }
         });
@@ -283,10 +289,8 @@
             void process() {
                 checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
-                    mNextDSDs = new ArrayList<DataSourceDesc>(1);
-                    mNextDSDs.add(dsd);
-                    mNextSrcId = mSrcIdGenerator++;
-                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourceInfos.clear();
+                    mNextSourceInfos.add(new SourceInfo(dsd));
                 }
                 prepareNextDataSource();
             }
@@ -309,9 +313,10 @@
                 }
 
                 synchronized (mSrcLock) {
-                    mNextDSDs = new ArrayList(dsds);
-                    mNextSrcId = mSrcIdGenerator++;
-                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mNextSourceInfos.clear();
+                    for (DataSourceDesc dsd : dsds) {
+                        mNextSourceInfos.add(new SourceInfo(dsd));
+                    }
                 }
                 prepareNextDataSource();
             }
@@ -323,22 +328,15 @@
         return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
-                synchronized (mSrcLock) {
-                    if (mNextDSDs != null) {
-                        mNextDSDs.clear();
-                        mNextDSDs = null;
-                    }
-                    mNextSrcId = mSrcIdGenerator++;
-                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
-                }
+                mNextSourceInfos.clear();
             }
         });
     }
 
     @Override
-    public @NonNull DataSourceDesc getCurrentDataSource() {
+    public DataSourceDesc getCurrentDataSource() {
         synchronized (mSrcLock) {
-            return mCurrentDSD;
+            return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
         }
     }
 
@@ -702,39 +700,35 @@
     // return true if there is a next data source, false otherwise.
     // This function should be always called on |mHandlerThread|.
     private boolean prepareNextDataSource() {
-        if (Looper.myLooper() != mHandlerThread.getLooper()) {
+        HandlerThread handlerThread = mHandlerThread;
+        if (handlerThread != null && Looper.myLooper() != handlerThread.getLooper()) {
             Log.e(TAG, "prepareNextDataSource: called on wrong looper");
         }
 
         boolean hasNextDSD;
-        synchronized (mSrcLock) {
-            hasNextDSD = (mNextDSDs != null && !mNextDSDs.isEmpty());
-        }
-
         int state = getState();
-        if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
-            // Current source has not been prepared yet.
-            return hasNextDSD;
-        }
-
         synchronized (mSrcLock) {
-            if (!hasNextDSD || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
+            hasNextDSD = !mNextSourceInfos.isEmpty();
+            if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
+                // Current source has not been prepared yet.
+                return hasNextDSD;
+            }
+
+            SourceInfo nextSource = mNextSourceInfos.peek();
+            if (!hasNextDSD || nextSource.mStateAsNextSource != NEXT_SOURCE_STATE_INIT) {
                 // There is no next source or it's in preparing or prepared state.
                 return hasNextDSD;
             }
 
             try {
-                mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
-                handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
+                nextSource.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARING;
+                handleDataSource(false /* isCurrent */, nextSource.mDSD, nextSource.mId);
             } catch (Exception e) {
                 Message msg = mTaskHandler.obtainMessage(
                         MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
-                mTaskHandler.handleMessage(msg, mNextSrcId);
+                mTaskHandler.handleMessage(msg, nextSource.mId);
 
-                mNextDSDs.remove(0);
-                // make a new SrcId to obsolete notification for previous one.
-                mNextSrcId = mSrcIdGenerator++;
-                mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                mNextSourceInfos.poll();
                 return prepareNextDataSource();
             }
         }
@@ -743,25 +737,21 @@
 
     // This function should be always called on |mHandlerThread|.
     private void playNextDataSource() {
-        if (Looper.myLooper() != mHandlerThread.getLooper()) {
+        HandlerThread handlerThread = mHandlerThread;
+        if (handlerThread != null && Looper.myLooper() != handlerThread.getLooper()) {
             Log.e(TAG, "playNextDataSource: called on wrong looper");
         }
 
         boolean hasNextDSD = false;
         synchronized (mSrcLock) {
-            if (mNextDSDs != null && !mNextDSDs.isEmpty()) {
+            if (!mNextSourceInfos.isEmpty()) {
                 hasNextDSD = true;
-                if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+                SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+                if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
                     // Switch to next source only when it has been prepared.
-                    mCurrentDSD = mNextDSDs.get(0);
-                    mCurrentSrcId = mNextSrcId;
-                    mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
-                    mNextDSDs.remove(0);
-                    mNextSrcId = mSrcIdGenerator++;  // make it different from |mCurrentSrcId|
-                    mBufferedPercentageNext.set(0);
-                    mNextSourceState = NEXT_SOURCE_STATE_INIT;
+                    mCurrentSourceInfo = mNextSourceInfos.poll();
 
-                    long srcId = mCurrentSrcId;
+                    long srcId = mCurrentSourceInfo.mId;
                     try {
                         nativePlayNextDataSource(srcId);
                     } catch (Exception e) {
@@ -776,9 +766,8 @@
 
                         // Now a new current src is playing.
                         // Wait for MEDIA_INFO_DATA_SOURCE_START to prepare next source.
-                        mNextSourcePlayPending = false;
                     }
-                } else if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
+                } else if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_INIT) {
                     hasNextDSD = prepareNextDataSource();
                 }
             }
@@ -1073,12 +1062,8 @@
             mDrmEventCallbackRecords.clear();
         }
         synchronized (mSrcLock) {
-            if (mNextDSDs != null) {
-                mNextDSDs.clear();
-                mNextDSDs = null;
-            }
-            mNextSrcId = mSrcIdGenerator++;
-            mNextSourceState = NEXT_SOURCE_STATE_INIT;
+            mCurrentSourceInfo = null;
+            mNextSourceInfos.clear();
         }
 
         synchronized (mTaskLock) {
@@ -1532,20 +1517,11 @@
             final int what = msg.arg1;
             final int extra = msg.arg2;
 
-            final DataSourceDesc dsd;
-            boolean isCurrentSrcId = false;
-            boolean isNextSrcId = false;
-            synchronized (mSrcLock) {
-                if (srcId == mCurrentSrcId) {
-                    dsd = mCurrentDSD;
-                    isCurrentSrcId = true;
-                } else if (mNextDSDs != null && !mNextDSDs.isEmpty() && srcId == mNextSrcId) {
-                    dsd = mNextDSDs.get(0);
-                    isNextSrcId = true;
-                } else {
-                    return;
-                }
+            final SourceInfo sourceInfo = getSourceInfoById(srcId);
+            if (sourceInfo == null) {
+                return;
             }
+            final DataSourceDesc dsd = sourceInfo.mDSD;
 
             switch(msg.what) {
                 case MEDIA_PREPARED:
@@ -1561,14 +1537,16 @@
                     }
 
                     synchronized (mSrcLock) {
+                        SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                         Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
-                                + ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
+                                + ", curSrc=" + mCurrentSourceInfo
+                                + ", nextSrc=" + nextSourceInfo);
 
-                        if (isCurrentSrcId) {
+                        if (isCurrentSource(srcId)) {
                             prepareNextDataSource();
-                        } else if (isNextSrcId) {
-                            mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
-                            if (mNextSourcePlayPending) {
+                        } else if (isNextSource(srcId)) {
+                            nextSourceInfo.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARED;
+                            if (nextSourceInfo.mPlayPendingAsNextSource) {
                                 playNextDataSource();
                             }
                         }
@@ -1621,7 +1599,7 @@
 
                 case MEDIA_PLAYBACK_COMPLETE:
                 {
-                    if (isCurrentSrcId) {
+                    if (isCurrentSource(srcId)) {
                         sendEvent(new EventNotifier() {
                             @Override
                             public void notify(EventCallback callback) {
@@ -1632,11 +1610,13 @@
                         stayAwake(false);
 
                         synchronized (mSrcLock) {
-                            mNextSourcePlayPending = true;
-
+                            SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+                            if (nextSourceInfo != null) {
+                                nextSourceInfo.mPlayPendingAsNextSource = true;
+                            }
                             Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
-                                    + ", currentSrcId=" + mCurrentSrcId
-                                    + ", nextSrcId=" + mNextSrcId);
+                                    + ", curSrc=" + mCurrentSourceInfo
+                                    + ", nextSrc=" + nextSourceInfo);
                         }
 
                         playNextDataSource();
@@ -1667,13 +1647,11 @@
                         }
                     });
 
-                    synchronized (mSrcLock) {
-                        if (isCurrentSrcId) {
-                            mBufferedPercentageCurrent.set(percent);
-                        } else if (isNextSrcId) {
-                            mBufferedPercentageNext.set(percent);
-                        }
+                    SourceInfo src = getSourceInfoById(srcId);
+                    if (src != null) {
+                        src.mBufferedPercentage.set(percent);
                     }
+
                     return;
                 }
 
@@ -1751,7 +1729,7 @@
                     });
 
                     if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
-                        if (isCurrentSrcId) {
+                        if (isCurrentSource(srcId)) {
                             prepareNextDataSource();
                         }
                     }
@@ -1854,6 +1832,7 @@
                 }
             }
         }
+
     }
 
     /*
@@ -2130,7 +2109,7 @@
                         @Override
                         public void notify(DrmEventCallback callback) {
                             callback.onDrmPrepared(
-                                    MediaPlayer2Impl.this, mCurrentDSD, prepareDrmStatus);
+                                    MediaPlayer2Impl.this, getCurrentDataSource(), prepareDrmStatus);
                         }
                     });
 
@@ -2196,7 +2175,7 @@
 
         // call the callback outside the lock
         if (mOnDrmConfigHelper != null)  {
-            mOnDrmConfigHelper.onDrmConfig(this, mCurrentDSD);
+            mOnDrmConfigHelper.onDrmConfig(this, getCurrentDataSource());
         }
 
         synchronized (mDrmLock) {
@@ -2817,7 +2796,7 @@
                 @Override
                 public void notify(DrmEventCallback callback) {
                     callback.onDrmPrepared(
-                            mediaPlayer, mCurrentDSD, status);
+                            mediaPlayer, getCurrentDataSource(), status);
                 }
             });
 
@@ -3084,9 +3063,7 @@
             } catch (Exception e) {
                 status = CALL_STATUS_ERROR_UNKNOWN;
             }
-            synchronized (mSrcLock) {
-                mDSD = mCurrentDSD;
-            }
+            mDSD = getCurrentDataSource();
 
             if (mMediaCallType != CALL_COMPLETED_SEEK_TO) {
                 synchronized (mTaskLock) {
@@ -3129,4 +3106,50 @@
             super(detailMessage);
         }
     };
+
+    private final class SourceInfo {
+        final DataSourceDesc mDSD;
+        final long mId = mSrcIdGenerator.getAndIncrement();
+        AtomicInteger mBufferedPercentage = new AtomicInteger(0);
+
+        // m*AsNextSource (below) only applies to pending data sources in the playlist;
+        // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
+        // are undefined.
+        int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
+        boolean mPlayPendingAsNextSource = false;
+
+        SourceInfo(DataSourceDesc dsd) {
+            this.mDSD = dsd;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s(%d)", SourceInfo.class.getName(), mId);
+        }
+
+    }
+
+    private SourceInfo getSourceInfoById(long srcId) {
+        synchronized (mSrcLock) {
+            if (isCurrentSource(srcId)) {
+                return mCurrentSourceInfo;
+            }
+            if (isNextSource(srcId)) {
+                return mNextSourceInfos.peek();
+            }
+        }
+        return null;
+    }
+
+    private boolean isCurrentSource(long srcId) {
+        synchronized (mSrcLock) {
+            return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
+        }
+    }
+
+    private boolean isNextSource(long srcId) {
+        SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+        return nextSourceInfo != null && nextSourceInfo.mId == srcId;
+    }
+
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 1136684..60153fc 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -78,8 +78,6 @@
 public class Assistant extends NotificationAssistantService {
     private static final String TAG = "ExtAssistant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    public static final boolean AUTO_DEMOTE_NOTIFICATIONS = SystemProperties.getBoolean(
-            "debug.demote_notifs", false);
     public static final boolean AGE_NOTIFICATIONS = SystemProperties.getBoolean(
             "debug.age_notifs", false);
 
@@ -242,7 +240,8 @@
         if (!smartReplies.isEmpty()) {
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
-        if (AUTO_DEMOTE_NOTIFICATIONS) {
+        if (Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
             if (mNotificationCategorizer.shouldSilence(entry)) {
                 final int importance = entry.getImportance() < IMPORTANCE_LOW
                         ? entry.getImportance() : IMPORTANCE_LOW;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
index 1aeb075..e824508 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
@@ -44,7 +44,14 @@
 
     @Override
     public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
-        action(context, category, "", taggedData);
+        final LogMaker logMaker = new LogMaker(category)
+                .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+        if (taggedData != null) {
+            for (Pair<Integer, Object> pair : taggedData) {
+                logMaker.addTaggedData(pair.first, pair.second);
+            }
+        }
+        MetricsLogger.action(logMaker);
     }
 
     @Override
@@ -58,19 +65,12 @@
     }
 
     @Override
-    public void action(Context context, int category, String pkg,
-            Pair<Integer, Object>... taggedData) {
-        if (taggedData == null || taggedData.length == 0) {
-            MetricsLogger.action(context, category, pkg);
-        } else {
-            final LogMaker logMaker = new LogMaker(category)
-                    .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
-                    .setPackageName(pkg);
-            for (Pair<Integer, Object> pair : taggedData) {
-                logMaker.addTaggedData(pair.first, pair.second);
-            }
-            MetricsLogger.action(logMaker);
-        }
+    public void action(Context context, int category, String pkg) {
+        final LogMaker logMaker = new LogMaker(category)
+                .setType(MetricsProto.MetricsEvent.TYPE_ACTION)
+                .setPackageName(pkg);
+
+        MetricsLogger.action(logMaker);
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index b60364e..f187688 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -51,7 +51,7 @@
     /**
      * Logs an user action.
      */
-    void action(Context context, int category, String pkg, Pair<Integer, Object>... taggedData);
+    void action(Context context, int category, String pkg);
 
     /**
      * Generically log action.
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 188204e..8cc3b5a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -79,7 +79,10 @@
         }
     }
 
-    public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
+    /**
+     * Logs a simple action without page id or attribution
+     */
+    public void action(Context context, int category,  Pair<Integer, Object>... taggedData) {
         for (LogWriter writer : mLoggerWriters) {
             writer.action(context, category, taggedData);
         }
@@ -88,10 +91,9 @@
     /**
      * Logs a generic Settings event.
      */
-    public void action(Context context, int category, String pkg,
-            Pair<Integer, Object>... taggedData) {
+    public void action(Context context, int category, String pkg) {
         for (LogWriter writer : mLoggerWriters) {
-            writer.action(context, category, pkg, taggedData);
+            writer.action(context, category, pkg);
         }
     }
 
@@ -135,16 +137,22 @@
                 // Not loggable
                 return;
             }
-            action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, action,
-                    Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+            action(sourceMetricsCategory,
+                    MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                    SettingsEnums.PAGE_UNKNOWN,
+                    action,
+                    0);
             return;
         } else if (TextUtils.equals(cn.getPackageName(), context.getPackageName())) {
             // Going to a Setting internal page, skip click logging in favor of page's own
             // visibility logging.
             return;
         }
-        action(context, MetricsEvent.ACTION_SETTINGS_TILE_CLICK, cn.flattenToString(),
-                Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
+        action(sourceMetricsCategory,
+                MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                SettingsEnums.PAGE_UNKNOWN,
+                cn.flattenToString(),
+                0);
     }
 
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
index 71f3789..320380f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
@@ -15,6 +15,7 @@
 package com.android.settingslib.core.instrumentation;
 
 import android.annotation.Nullable;
+import android.app.settings.SettingsEnums;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -22,12 +23,9 @@
 import android.os.AsyncTask;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentSkipListSet;
@@ -117,10 +115,9 @@
             return;
         }
 
-        final Pair<Integer, Object> valueData;
+        final int intVal;
         if (value instanceof Long) {
             final Long longVal = (Long) value;
-            final int intVal;
             if (longVal > Integer.MAX_VALUE) {
                 intVal = Integer.MAX_VALUE;
             } else if (longVal < Integer.MIN_VALUE) {
@@ -128,47 +125,45 @@
             } else {
                 intVal = longVal.intValue();
             }
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    intVal);
         } else if (value instanceof Integer) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    value);
+            intVal = (int) value;
         } else if (value instanceof Boolean) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
-                    (Boolean) value ? 1 : 0);
+            intVal = (Boolean) value ? 1 : 0;
         } else if (value instanceof Float) {
-            valueData = Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE,
-                    value);
-        } else if (value instanceof String) {
-            Log.d(LOG_TAG, "Tried to log string preference " + prefKey + " = " + value);
-            valueData = null;
+            final float floatValue = (float) value;
+            if (floatValue > Integer.MAX_VALUE) {
+                intVal = Integer.MAX_VALUE;
+            } else if (floatValue < Integer.MIN_VALUE) {
+                intVal = Integer.MIN_VALUE;
+            } else {
+                intVal = (int) floatValue;
+            }
         } else {
             Log.w(LOG_TAG, "Tried to log unloggable object" + value);
-            valueData = null;
+            return;
         }
-        if (valueData != null) {
-            // Pref key exists in set, log it's change in metrics.
-            mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE,
-                    Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey),
-                    valueData);
-        }
+        // Pref key exists in set, log it's change in metrics.
+        mMetricsFeature.action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                prefKey,
+                intVal);
     }
 
     @VisibleForTesting
     void logPackageName(String key, String value) {
         final String prefKey = mTag + "/" + key;
-        mMetricsFeature.action(mContext, MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE, value,
-                Pair.create(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, prefKey));
+        mMetricsFeature.action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                prefKey + ":" + value,
+                0);
     }
 
     private void safeLogValue(String key, String value) {
         new AsyncPackageCheck().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key, value);
     }
 
-    public static String buildCountName(String prefKey, Object value) {
-        return prefKey + "|" + value;
-    }
-
     public static String buildPrefKey(String tag, String key) {
         return tag + "/" + key;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 603f838..4ec6fb2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -17,8 +17,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
@@ -27,7 +25,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.util.Pair;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -77,10 +74,11 @@
         mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
 
         verify(mLogWriter).action(
-                eq(mContext),
-                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
-                anyString(),
-                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
+                MetricsEvent.SETTINGS_GESTURES,
+                MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                SettingsEnums.PAGE_UNKNOWN,
+                Intent.ACTION_ASSIST,
+                0);
     }
 
     @Test
@@ -90,10 +88,11 @@
         mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
 
         verify(mLogWriter).action(
-                eq(mContext),
-                eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
-                anyString(),
-                eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
+                MetricsEvent.SETTINGS_GESTURES,
+                MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                SettingsEnums.PAGE_UNKNOWN,
+                "pkg/cls",
+                0);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index be671e6..6285fcd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -15,25 +15,16 @@
  */
 package com.android.settingslib.core.instrumentation;
 
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
-        .ACTION_SETTINGS_PREFERENCE_CHANGE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
-        .FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
-        .FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent
-        .FIELD_SETTINGS_PREFERENCE_CHANGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.util.Pair;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
@@ -41,7 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -50,11 +40,11 @@
 
     private static final String TEST_TAG = "tag";
     private static final String TEST_KEY = "key";
+    private static final String TEST_TAGGED_KEY = TEST_TAG + "/" + TEST_KEY;
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private Context mContext;
 
-    private ArgumentMatcher<Pair<Integer, Object>> mNamePairMatcher;
     @Mock
     private MetricsFeatureProvider mMetricsFeature;
     private SharedPreferencesLogger mSharedPrefLogger;
@@ -63,7 +53,6 @@
     public void init() {
         MockitoAnnotations.initMocks(this);
         mSharedPrefLogger = new SharedPreferencesLogger(mContext, TEST_TAG, mMetricsFeature);
-        mNamePairMatcher = pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_NAME, String.class);
     }
 
     @Test
@@ -77,9 +66,11 @@
         editor.putInt(TEST_KEY, 2);
         editor.putInt(TEST_KEY, 2);
 
-        verify(mMetricsFeature, times(6)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
+        verify(mMetricsFeature, times(6)).action(eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
+                eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(TEST_TAGGED_KEY),
+                anyInt());
     }
 
     @Test
@@ -92,12 +83,16 @@
         editor.putBoolean(TEST_KEY, false);
 
 
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, true)));
-        verify(mMetricsFeature, times(3)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, false)));
+        verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                TEST_TAGGED_KEY,
+                1);
+        verify(mMetricsFeature, times(3)).action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                TEST_TAGGED_KEY,
+                0);
     }
 
     @Test
@@ -109,9 +104,11 @@
         editor.putLong(TEST_KEY, 1);
         editor.putLong(TEST_KEY, 2);
 
-        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.class)));
+        verify(mMetricsFeature, times(4)).action(eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
+                eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(TEST_TAGGED_KEY),
+                anyInt());
     }
 
     @Test
@@ -121,10 +118,11 @@
         editor.putLong(TEST_KEY, 1);
         editor.putLong(TEST_KEY, veryBigNumber);
 
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(
-                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MAX_VALUE)));
+        verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                TEST_TAGGED_KEY,
+                Integer.MAX_VALUE);
     }
 
     @Test
@@ -134,10 +132,10 @@
         editor.putLong(TEST_KEY, 1);
         editor.putLong(TEST_KEY, veryNegativeNumber);
 
-        verify(mMetricsFeature).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(
-                        FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE, Integer.MIN_VALUE)));
+        verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
+                SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                TEST_TAGGED_KEY, Integer.MIN_VALUE);
     }
 
     @Test
@@ -149,38 +147,20 @@
         editor.putFloat(TEST_KEY, 1);
         editor.putFloat(TEST_KEY, 2);
 
-        verify(mMetricsFeature, times(4)).action(any(Context.class), anyInt(),
-                argThat(mNamePairMatcher),
-                argThat(pairMatches(FIELD_SETTINGS_PREFERENCE_CHANGE_FLOAT_VALUE, Float.class)));
+        verify(mMetricsFeature, times(4)).action(eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
+                eq(SettingsEnums.PAGE_UNKNOWN),
+                eq(TEST_TAGGED_KEY),
+                anyInt());
     }
 
     @Test
     public void logPackage_shouldUseLogPackageApi() {
         mSharedPrefLogger.logPackageName("key", "com.android.settings");
-        verify(mMetricsFeature).action(any(Context.class),
-                eq(ACTION_SETTINGS_PREFERENCE_CHANGE),
-                eq("com.android.settings"),
-                any(Pair.class));
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, Class clazz) {
-        return pair -> pair.first == tag && isInstanceOfType(pair.second, clazz);
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, boolean bool) {
-        return pair -> pair.first == tag
-                && isInstanceOfType(pair.second, Integer.class)
-                && pair.second.equals((bool ? 1 : 0));
-    }
-
-    private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag, int val) {
-        return pair -> pair.first == tag
-                && isInstanceOfType(pair.second, Integer.class)
-                && pair.second.equals(val);
-    }
-
-    /** Returns true if the instance is assignable to the type Clazz. */
-    private static boolean isInstanceOfType(Object instance, Class<?> clazz) {
-        return clazz.isInstance(instance);
+        verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
+                ACTION_SETTINGS_PREFERENCE_CHANGE,
+                SettingsEnums.PAGE_UNKNOWN,
+                "tag/key:com.android.settings",
+                0);
     }
 }
diff --git a/packages/SystemUI/res/layout/navigation_bar_window.xml b/packages/SystemUI/res/layout/navigation_bar_window.xml
index 6fa46d4..f98cbd8 100644
--- a/packages/SystemUI/res/layout/navigation_bar_window.xml
+++ b/packages/SystemUI/res/layout/navigation_bar_window.xml
@@ -20,6 +20,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/navigation_bar_frame"
+    android:theme="@style/Theme.SystemUI"
     android:layout_height="match_parent"
     android:layout_width="match_parent">
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index fbcf068..5e6d272 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -296,7 +296,7 @@
                 new WakefulnessLifecycle());
 
         mProviders.put(FragmentService.class, () ->
-                new FragmentService(mContext));
+                new FragmentService());
 
         mProviders.put(ExtensionController.class, () ->
                 new ExtensionControllerImpl(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 0ed1cd1..779a86c 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -59,11 +59,11 @@
     private FragmentController mFragments;
     private FragmentLifecycleCallbacks mLifecycleCallbacks;
 
-    FragmentHostManager(Context context, FragmentService manager, View rootView) {
-        mContext = context;
+    FragmentHostManager(FragmentService manager, View rootView) {
+        mContext = rootView.getContext();
         mManager = manager;
         mRootView = rootView;
-        mConfigChanges.applyNewConfig(context.getResources());
+        mConfigChanges.applyNewConfig(mContext.getResources());
         createFragmentHost(null);
     }
 
@@ -203,6 +203,10 @@
         }
     }
 
+    public static void removeAndDestroy(View view) {
+        Dependency.get(FragmentService.class).removeAndDestroy(view);
+    }
+
     class HostCallbacks extends FragmentHostCallback<FragmentHostManager> {
         public HostCallbacks() {
             super(mContext, FragmentHostManager.this.mHandler, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index f9bf4f5..bf7d629 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -14,18 +14,13 @@
 
 package com.android.systemui.fragments;
 
-import android.content.Context;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.os.Handler;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.view.View;
 
 import com.android.systemui.ConfigurationChangedReceiver;
 import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -40,11 +35,6 @@
 
     private final ArrayMap<View, FragmentHostState> mHosts = new ArrayMap<>();
     private final Handler mHandler = new Handler();
-    private final Context mContext;
-
-    public FragmentService(Context context) {
-        mContext = context;
-    }
 
     public FragmentHostManager getFragmentHostManager(View view) {
         View root = view.getRootView();
@@ -56,6 +46,13 @@
         return state.getFragmentHostManager();
     }
 
+    public void removeAndDestroy(View view) {
+        final FragmentHostState state = mHosts.remove(view.getRootView());
+        if (state != null) {
+            state.mFragmentHostManager.destroy();
+        }
+    }
+
     public void destroyAll() {
         for (FragmentHostState state : mHosts.values()) {
             state.mFragmentHostManager.destroy();
@@ -84,7 +81,7 @@
 
         public FragmentHostState(View view) {
             mView = view;
-            mFragmentHostManager = new FragmentHostManager(mContext, FragmentService.this, mView);
+            mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView);
         }
 
         public void sendConfigurationChange(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index fe1b356..3b9110d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1940,11 +1940,6 @@
                     mContext.getSystemService(Context.STATUS_BAR_SERVICE);
         }
 
-        // TODO(b/113914868): investigation log for disappearing home button
-        Log.d(TAG, "adjustStatusBarLocked (b/113914868): mShowing=" + mShowing
-                + " mStatusBarManager=" + mStatusBarManager + " mOccluded="
-                + mOccluded + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons);
-
         if (mStatusBarManager == null) {
             Log.w(TAG, "Could not get status bar manager");
         } else {
@@ -1961,6 +1956,12 @@
                         +  " --> flags=0x" + Integer.toHexString(flags));
             }
 
+            // TODO(b/113914868): investigation log for disappearing home button
+            Log.d(TAG, "adjustStatusBarLocked (b/113914868): flags=" + flags
+                    + "mShowing=" + mShowing + " mStatusBarManager=" + mStatusBarManager
+                    + " mOccluded=" + mOccluded + " isSecure=" + isSecure()
+                    + " force=" + forceHideHomeRecentsButtons);
+
             mStatusBarManager.disable(flags);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 235629b..9fdb534 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -268,6 +268,8 @@
     }
 
     public void hide(boolean destroyView) {
+        // TODO(b/113914868): investigation log for disappearing home button
+        Log.i(TAG, "KeyguardBouncer.hide (b/113914868): destroyView=" + destroyView);
         if (isShowing()) {
             StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
                 StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b0ac6ec..b29889d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
+import android.util.MathUtils;
 import android.util.TimeUtils;
 
 import com.android.systemui.Dependency;
@@ -29,6 +30,7 @@
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 import java.io.FileDescriptor;
@@ -37,7 +39,8 @@
 /**
  * Class to control all aspects about light bar changes.
  */
-public class LightBarTransitionsController implements Dumpable, Callbacks {
+public class LightBarTransitionsController implements Dumpable, Callbacks,
+        StatusBarStateController.StateListener {
 
     public static final long DEFAULT_TINT_ANIMATION_DURATION = 120;
     private static final String EXTRA_DARK_INTENSITY = "dark_intensity";
@@ -45,6 +48,7 @@
     private final Handler mHandler;
     private final DarkIntensityApplier mApplier;
     private final KeyguardMonitor mKeyguardMonitor;
+    private final StatusBarStateController mStatusBarStateController;
 
     private boolean mTransitionDeferring;
     private long mTransitionDeferringStartTime;
@@ -55,6 +59,7 @@
     private ValueAnimator mTintAnimator;
     private float mDarkIntensity;
     private float mNextDarkIntensity;
+    private float mDozeAmount;
     private final Runnable mTransitionDeferringDoneRunnable = new Runnable() {
         @Override
         public void run() {
@@ -66,13 +71,17 @@
         mApplier = applier;
         mHandler = new Handler();
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+        mStatusBarStateController = Dependency.get(StatusBarStateController.class);
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
                 .addCallbacks(this);
+        mStatusBarStateController.addListener(this);
+        mDozeAmount = mStatusBarStateController.getDozeAmount();
     }
 
     public void destroy(Context context) {
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
                 .removeCallbacks(this);
+        mStatusBarStateController.removeListener(this);
     }
 
     public void saveState(Bundle outState) {
@@ -173,7 +182,11 @@
 
     private void setIconTintInternal(float darkIntensity) {
         mDarkIntensity = darkIntensity;
-        mApplier.applyDarkIntensity(darkIntensity);
+        dispatchDark();
+    }
+
+    private void dispatchDark() {
+        mApplier.applyDarkIntensity(MathUtils.lerp(mDarkIntensity, 0f, mDozeAmount));
     }
 
     @Override
@@ -196,6 +209,15 @@
         pw.print(" mNextDarkIntensity="); pw.println(mNextDarkIntensity);
     }
 
+    @Override
+    public void onStateChanged(int newState) { }
+
+    @Override
+    public void onDozeAmountChanged(float linear, float eased) {
+        mDozeAmount = eased;
+        dispatchDark();
+    }
+
     /**
      * Interface to apply a specific dark intensity.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 4406b14..0cf1b3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -144,6 +144,7 @@
 
     private OverviewProxyService mOverviewProxyService;
 
+    private boolean mIsOnDefaultDisplay = true;
     public boolean mHomeBlockedThisTouch;
 
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@@ -241,6 +242,11 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mNavigationBarView = (NavigationBarView) view;
+        final Display display = view.getDisplay();
+        // It may not have display when running unit test.
+        if (display != null) {
+            mIsOnDefaultDisplay = display.getDisplayId() == Display.DEFAULT_DISPLAY;
+        }
 
         mNavigationBarView.setComponents(mStatusBar.getPanel());
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
@@ -253,8 +259,6 @@
         prepareNavigationBarView();
         checkNavBarModes();
 
-        setDisabled2Flags(mDisabledFlags2);
-
         IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -262,16 +266,23 @@
         notifyNavigationBarScreenOn();
         mOverviewProxyService.addCallback(mOverviewProxyListener);
 
-        RotationContextButton rotationButton = mNavigationBarView.getRotateSuggestionButton();
-        rotationButton.setListener(mRotationButtonListener);
-        rotationButton.addRotationCallback(mRotationWatcher);
+        // Currently there is no accelerometer sensor on non-default display.
+        if (mIsOnDefaultDisplay) {
+            final RotationContextButton rotationButton =
+                    mNavigationBarView.getRotateSuggestionButton();
+            rotationButton.setListener(mRotationButtonListener);
+            rotationButton.addRotationCallback(mRotationWatcher);
 
-        // Reset user rotation pref to match that of the WindowManager if starting in locked mode
-        // This will automatically happen when switching from auto-rotate to locked mode
-        if (rotationButton.isRotationLocked()) {
-            final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
-            rotationButton.setRotationLockedAtAngle(winRotation);
+            // Reset user rotation pref to match that of the WindowManager if starting in locked
+            // mode. This will automatically happen when switching from auto-rotate to locked mode.
+            if (display != null && rotationButton.isRotationLocked()) {
+                final int winRotation = display.getRotation();
+                rotationButton.setRotationLockedAtAngle(winRotation);
+            }
+        } else {
+            mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
         }
+        setDisabled2Flags(mDisabledFlags2);
     }
 
     @Override
@@ -389,7 +400,7 @@
 
     @Override
     public void onRotationProposal(final int rotation, boolean isValid) {
-        final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
+        final int winRotation = mNavigationBarView.getDisplay().getRotation();
         final boolean rotateSuggestionsDisabled = RotationContextButton
                 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
         if (RotationContextButton.DEBUG_ROTATION) {
@@ -477,10 +488,13 @@
             updateScreenPinningGestures();
         }
 
-        final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
-        if (masked2 != mDisabledFlags2) {
-            mDisabledFlags2 = masked2;
-            setDisabled2Flags(masked2);
+        // Only default display supports rotation suggestions.
+        if (mIsOnDefaultDisplay) {
+            final int masked2 = state2 & (StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS);
+            if (masked2 != mDisabledFlags2) {
+                mDisabledFlags2 = masked2;
+                setDisabled2Flags(masked2);
+            }
         }
     }
 
@@ -881,13 +895,23 @@
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
         if (navigationBarView == null) return null;
 
+        final NavigationBarFragment fragment = new NavigationBarFragment();
+        navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+                fragmentHost.getFragmentManager().beginTransaction()
+                        .replace(R.id.navigation_bar_frame, fragment, TAG)
+                        .commit();
+                fragmentHost.addTagListener(TAG, listener);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                FragmentHostManager.removeAndDestroy(v);
+            }
+        });
         context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
-        FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
-        NavigationBarFragment fragment = new NavigationBarFragment();
-        fragmentHost.getFragmentManager().beginTransaction()
-                .replace(R.id.navigation_bar_frame, fragment, TAG)
-                .commit();
-        fragmentHost.addTagListener(TAG, listener);
         return navigationBarView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 2c3c27f..b43bbdc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -264,8 +264,7 @@
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mDisplay = ((WindowManager) context.getSystemService(
-                Context.WINDOW_SERVICE)).getDefaultDisplay();
+        mDisplay = context.getDisplay();
 
         mVertical = false;
         mLongClickableAccessibilityButton = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 12fbf2d..a6a9d74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -342,7 +342,8 @@
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     protected StatusBarWindowController mStatusBarWindowController;
     protected UnlockMethodCache mUnlockMethodCache;
-    private DozeServiceHost mDozeServiceHost = new DozeServiceHost();
+    @VisibleForTesting
+    DozeServiceHost mDozeServiceHost = new DozeServiceHost();
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
 
@@ -479,7 +480,7 @@
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
-    private PowerManager.WakeLock mGestureWakeLock;
+    protected PowerManager.WakeLock mGestureWakeLock;
     private Vibrator mVibrator;
     private long[] mCameraLaunchGestureVibePattern;
 
@@ -3609,6 +3610,7 @@
         }
     }
 
+    @VisibleForTesting
     final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
         @Override
         public void onFinishedGoingToSleep() {
@@ -3650,6 +3652,7 @@
             mNotificationPanel.setTouchAndAnimationDisabled(false);
             updateVisibleToUser();
             updateIsKeyguard();
+            mDozeServiceHost.stopDozing();
         }
     };
 
@@ -3856,7 +3859,8 @@
         return mStatusBarKeyguardViewManager.isShowing();
     }
 
-    private final class DozeServiceHost implements DozeHost {
+    @VisibleForTesting
+    final class DozeServiceHost implements DozeHost {
         private final ArrayList<Callback> mCallbacks = new ArrayList<>();
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
@@ -3944,7 +3948,6 @@
                 mDozingRequested = false;
                 DozeLog.traceDozing(mContext, mDozing);
                 updateDozing();
-                mWakefulnessLifecycle.dispatchStartedWakingUp();
             }
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
new file mode 100644
index 0000000..0031c32
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LightBarTransitionsControllerTest extends SysuiTestCase {
+
+    @Mock
+    private DarkIntensityApplier mApplier;
+    private LightBarTransitionsController mLightBarTransitionsController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+        mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier);
+    }
+
+    @Test
+    public void setIconsDark_lightAndDark() {
+        mLightBarTransitionsController.setIconsDark(true /* dark */, false /* animate */);
+        verify(mApplier).applyDarkIntensity(eq(1f));
+
+        mLightBarTransitionsController.setIconsDark(false /* dark */, false /* animate */);
+        verify(mApplier).applyDarkIntensity(eq(0f));
+    }
+
+    @Test
+    public void onDozeAmountChanged_lightWhenDozing() {
+        mLightBarTransitionsController.onDozeAmountChanged(1f /* linear */, 1f /* eased */);
+        mLightBarTransitionsController.setIconsDark(true /* dark */, false /* animate */);
+        verify(mApplier, times(2)).applyDarkIntensity(eq(0f));
+
+        reset(mApplier);
+        mLightBarTransitionsController.setIconsDark(false /* dark */, false /* animate */);
+        verify(mApplier).applyDarkIntensity(eq(0f));
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 882f261..d442de2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -593,6 +593,30 @@
         verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER));
     }
 
+    @Test
+    public void testStartStopDozing() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
+
+        mStatusBar.mDozeServiceHost.startDozing();
+        verify(mStatusBarStateController).setIsDozing(eq(true));
+
+        mStatusBar.mDozeServiceHost.stopDozing();
+        verify(mStatusBarStateController).setIsDozing(eq(false));
+    }
+
+    @Test
+    public void testOnStartedWakingUp_isNotDozing() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
+
+        mStatusBar.mDozeServiceHost.startDozing();
+        verify(mStatusBarStateController).setIsDozing(eq(true));
+
+        mStatusBar.mWakefulnessObserver.onStartedWakingUp();
+        verify(mStatusBarStateController).setIsDozing(eq(false));
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -642,6 +666,7 @@
             mLockscreenUserManager = notificationLockscreenUserManager;
             mCommandQueue = commandQueue;
             mPresenter = notificationPresenter;
+            mGestureWakeLock = mock(PowerManager.WakeLock.class);
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 033e996..11963d2 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1674,4 +1674,20 @@
 
   // Total time the wifi radio is scanning in ms over the logging duration.
   optional int64 radio_scan_time_ms = 5;
+
+  // Total time the wifi radio spent doing nan scans in ms over the logging duration.
+  optional int64 radio_nan_scan_time_ms = 6;
+
+  // Total time the wifi radio spent doing background scans in ms over the logging duration.
+  optional int64 radio_background_scan_time_ms = 7;
+
+  // Total time the wifi radio spent doing roam scans in ms over the logging duration.
+  optional int64 radio_roam_scan_time_ms = 8;
+
+  // Total time the wifi radio spent doing pno scans in ms over the logging duration.
+  optional int64 radio_pno_scan_time_ms = 9;
+
+  // Total time the wifi radio spent doing hotspot 2.0 scans and GAS exchange
+  // in ms over the logging duration.
+  optional int64 radio_hs20_scan_time_ms = 10;
 }
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
new file mode 100644
index 0000000..18011f6
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import com.android.server.backup.encryption.chunking.Chunker;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+
+/** Splits a stream of bytes into variable-sized chunks, using content-defined chunking. */
+public class ContentDefinedChunker implements Chunker {
+    private static final int WINDOW_SIZE = 31;
+    private static final byte DEFAULT_OUT_BYTE = (byte) 0;
+
+    private final byte[] mChunkBuffer;
+    private final RabinFingerprint64 mRabinFingerprint64;
+    private final FingerprintMixer mFingerprintMixer;
+    private final BreakpointPredicate mBreakpointPredicate;
+    private final int mMinChunkSize;
+    private final int mMaxChunkSize;
+
+    /**
+     * Constructor.
+     *
+     * @param minChunkSize The minimum size of a chunk. No chunk will be produced of a size smaller
+     *     than this except possibly at the very end of the stream.
+     * @param maxChunkSize The maximum size of a chunk. No chunk will be produced of a larger size.
+     * @param rabinFingerprint64 Calculates fingerprints, with which to determine breakpoints.
+     * @param breakpointPredicate Given a Rabin fingerprint, returns whether this ought to be a
+     *     breakpoint.
+     */
+    public ContentDefinedChunker(
+            int minChunkSize,
+            int maxChunkSize,
+            RabinFingerprint64 rabinFingerprint64,
+            FingerprintMixer fingerprintMixer,
+            BreakpointPredicate breakpointPredicate) {
+        checkArgument(
+                minChunkSize >= WINDOW_SIZE,
+                "Minimum chunk size must be greater than window size.");
+        checkArgument(
+                maxChunkSize >= minChunkSize,
+                "Maximum chunk size cannot be smaller than minimum chunk size.");
+        mChunkBuffer = new byte[maxChunkSize];
+        mRabinFingerprint64 = rabinFingerprint64;
+        mBreakpointPredicate = breakpointPredicate;
+        mFingerprintMixer = fingerprintMixer;
+        mMinChunkSize = minChunkSize;
+        mMaxChunkSize = maxChunkSize;
+    }
+
+    /**
+     * Breaks the input stream into variable-sized chunks.
+     *
+     * @param inputStream The input bytes to break into chunks.
+     * @param chunkConsumer A function to process each chunk as it's generated.
+     * @throws IOException Thrown if there is an issue reading from the input stream.
+     * @throws GeneralSecurityException Thrown if the {@link ChunkConsumer} throws it.
+     */
+    @Override
+    public void chunkify(InputStream inputStream, ChunkConsumer chunkConsumer)
+            throws IOException, GeneralSecurityException {
+        int chunkLength;
+        int initialReadLength = mMinChunkSize - WINDOW_SIZE;
+
+        // Performance optimization - there is no reason to calculate fingerprints for windows
+        // ending before the minimum chunk size.
+        while ((chunkLength =
+                        inputStream.read(mChunkBuffer, /*off=*/ 0, /*len=*/ initialReadLength))
+                != -1) {
+            int b;
+            long fingerprint = 0L;
+
+            while ((b = inputStream.read()) != -1) {
+                byte inByte = (byte) b;
+                byte outByte = getCurrentWindowStartByte(chunkLength);
+                mChunkBuffer[chunkLength++] = inByte;
+
+                fingerprint =
+                        mRabinFingerprint64.computeFingerprint64(inByte, outByte, fingerprint);
+
+                if (chunkLength >= mMaxChunkSize
+                        || (chunkLength >= mMinChunkSize
+                                && mBreakpointPredicate.isBreakpoint(
+                                        mFingerprintMixer.mix(fingerprint)))) {
+                    chunkConsumer.accept(Arrays.copyOf(mChunkBuffer, chunkLength));
+                    chunkLength = 0;
+                    break;
+                }
+            }
+
+            if (chunkLength > 0) {
+                chunkConsumer.accept(Arrays.copyOf(mChunkBuffer, chunkLength));
+            }
+        }
+    }
+
+    private byte getCurrentWindowStartByte(int chunkLength) {
+        if (chunkLength < mMinChunkSize) {
+            return DEFAULT_OUT_BYTE;
+        } else {
+            return mChunkBuffer[chunkLength - WINDOW_SIZE];
+        }
+    }
+
+    /** Whether the current fingerprint indicates the end of a chunk. */
+    public interface BreakpointPredicate {
+
+        /**
+         * Returns {@code true} if the fingerprint of the last {@code WINDOW_SIZE} bytes indicates
+         * the chunk ought to end at this position.
+         *
+         * @param fingerprint Fingerprint of the last {@code WINDOW_SIZE} bytes.
+         * @return Whether this ought to be a chunk breakpoint.
+         */
+        boolean isBreakpoint(long fingerprint);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
new file mode 100644
index 0000000..e9f3050
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Helper for mixing fingerprint with key material.
+ *
+ * <p>We do this as otherwise the Rabin fingerprint leaks information about the plaintext. i.e., if
+ * two users have the same file, it will be partitioned by Rabin in the same way, allowing us to
+ * infer that it is the same as another user's file.
+ *
+ * <p>By mixing the fingerprint with the user's secret key, the chunking method is different on a
+ * per key basis. Each application has its own {@link SecretKey}, so we cannot infer that a file is
+ * the same even across multiple applications owned by the same user, never mind across multiple
+ * users.
+ *
+ * <p>Instead of directly mixing the fingerprint with the user's secret, we first securely and
+ * deterministically derive a secondary chunking key. As Rabin is not a cryptographically secure
+ * hash, it might otherwise leak information about the user's secret. This prevents that from
+ * happening.
+ */
+public class FingerprintMixer {
+    public static final int SALT_LENGTH_BYTES = 256 / Byte.SIZE;
+    private static final String DERIVED_KEY_NAME = "RabinFingerprint64Mixer";
+
+    private final long mAddend;
+    private final long mMultiplicand;
+
+    /**
+     * A new instance from a given secret key and salt. Salt must be the same across incremental
+     * backups, or a different chunking strategy will be used each time, defeating the dedup.
+     *
+     * @param secretKey The application-specific secret.
+     * @param salt The salt.
+     * @throws InvalidKeyException If the encoded form of {@code secretKey} is inaccessible.
+     */
+    public FingerprintMixer(SecretKey secretKey, byte[] salt) throws InvalidKeyException {
+        checkArgument(salt.length == SALT_LENGTH_BYTES, "Requires a 256-bit salt.");
+        byte[] keyBytes = secretKey.getEncoded();
+        if (keyBytes == null) {
+            throw new InvalidKeyException("SecretKey must support encoding for FingerprintMixer.");
+        }
+        byte[] derivedKey =
+                Hkdf.hkdf(keyBytes, salt, DERIVED_KEY_NAME.getBytes(StandardCharsets.UTF_8));
+        ByteBuffer buffer = ByteBuffer.wrap(derivedKey);
+        mAddend = buffer.getLong();
+        // Multiplicand must be odd - otherwise we lose some bits of the Rabin fingerprint when
+        // mixing
+        mMultiplicand = buffer.getLong() | 1;
+    }
+
+    /**
+     * Mixes the fingerprint with the derived key material. This is performed by adding part of the
+     * derived key and multiplying by another part of the derived key (which is forced to be odd, so
+     * that the operation is reversible).
+     *
+     * @param fingerprint A 64-bit Rabin fingerprint.
+     * @return The mixed fingerprint.
+     */
+    long mix(long fingerprint) {
+        return ((fingerprint + mAddend) * mMultiplicand);
+    }
+
+    /** The addend part of the derived key. */
+    long getAddend() {
+        return mAddend;
+    }
+
+    /** The multiplicand part of the derived key. */
+    long getMultiplicand() {
+        return mMultiplicand;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
new file mode 100644
index 0000000..6f4f549
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Secure HKDF utils. Allows client to deterministically derive additional key material from a base
+ * secret. If the derived key material is compromised, this does not in of itself compromise the
+ * root secret.
+ *
+ * <p>TODO(b/116575321): After all code is ported, rename this class to HkdfUtils.
+ */
+public final class Hkdf {
+    private static final byte[] CONSTANT_01 = {0x01};
+    private static final String HmacSHA256 = "HmacSHA256";
+    private static final String AES = "AES";
+
+    /**
+     * Implements HKDF (RFC 5869) with the SHA-256 hash and a 256-bit output key length.
+     *
+     * <p>IMPORTANT: The use or edit of this method requires a security review.
+     *
+     * @param masterKey Master key from which to derive sub-keys.
+     * @param salt A randomly generated 256-bit byte string.
+     * @param data Arbitrary information that is bound to the derived key (i.e., used in its
+     *     creation).
+     * @return Raw derived key bytes = HKDF-SHA256(masterKey, salt, data).
+     * @throws InvalidKeyException If the salt can not be used as a valid key.
+     */
+    static byte[] hkdf(byte[] masterKey, byte[] salt, byte[] data) throws InvalidKeyException {
+        checkNotNull(masterKey, "HKDF requires master key to be set.");
+        checkNotNull(salt, "HKDF requires a salt.");
+        checkNotNull(data, "No data provided to HKDF.");
+        return hkdfSha256Expand(hkdfSha256Extract(masterKey, salt), data);
+    }
+
+    private Hkdf() {}
+
+    /**
+     * The HKDF (RFC 5869) extraction function, using the SHA-256 hash function. This function is
+     * used to pre-process the {@code inputKeyMaterial} and mix it with the {@code salt}, producing
+     * output suitable for use with HKDF expansion function (which produces the actual derived key).
+     *
+     * <p>IMPORTANT: The use or edit of this method requires a security review.
+     *
+     * @see #hkdfSha256Expand(byte[], byte[])
+     * @return HMAC-SHA256(salt, inputKeyMaterial) (salt is the "key" for the HMAC)
+     * @throws InvalidKeyException If the salt can not be used as a valid key.
+     */
+    private static byte[] hkdfSha256Extract(byte[] inputKeyMaterial, byte[] salt)
+            throws InvalidKeyException {
+        // Note that the SecretKey encoding format is defined to be RAW, so the encoded form should
+        // be consistent across implementations.
+        Mac sha256;
+        try {
+            sha256 = Mac.getInstance(HmacSHA256);
+        } catch (NoSuchAlgorithmException e) {
+            // This can not happen - HmacSHA256 is supported by the platform.
+            throw new AssertionError(e);
+        }
+        sha256.init(new SecretKeySpec(salt, AES));
+
+        return sha256.doFinal(inputKeyMaterial);
+    }
+
+    /**
+     * Special case of HKDF (RFC 5869) expansion function, using the SHA-256 hash function and
+     * allowing for a maximum output length of 256 bits.
+     *
+     * <p>IMPORTANT: The use or edit of this method requires a security review.
+     *
+     * @param pseudoRandomKey Generated by {@link #hkdfSha256Extract(byte[], byte[])}.
+     * @param info Arbitrary information the derived key should be bound to.
+     * @return Raw derived key bytes = HMAC-SHA256(pseudoRandomKey, info | 0x01).
+     * @throws InvalidKeyException If the salt can not be used as a valid key.
+     */
+    private static byte[] hkdfSha256Expand(byte[] pseudoRandomKey, byte[] info)
+            throws InvalidKeyException {
+        // Note that RFC 5869 computes number of blocks N = ceil(hash length / output length), but
+        // here we only deal with a 256 bit hash up to a 256 bit output, yielding N=1.
+        Mac sha256;
+        try {
+            sha256 = Mac.getInstance(HmacSHA256);
+        } catch (NoSuchAlgorithmException e) {
+            // This can not happen - HmacSHA256 is supported by the platform.
+            throw new AssertionError(e);
+        }
+        sha256.init(new SecretKeySpec(pseudoRandomKey, AES));
+
+        sha256.update(info);
+        sha256.update(CONSTANT_01);
+        return sha256.doFinal();
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
new file mode 100644
index 0000000..e867e7c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import com.android.server.backup.encryption.chunking.cdc.ContentDefinedChunker.BreakpointPredicate;
+
+/**
+ * Function to determine whether a 64-bit fingerprint ought to be a chunk breakpoint.
+ *
+ * <p>This works by checking whether there are at least n leading zeros in the fingerprint. n is
+ * calculated to on average cause a breakpoint after a given number of trials (provided in the
+ * constructor). This allows us to choose a number of trials that gives a desired average chunk
+ * size. This works because the fingerprint is pseudo-randomly distributed.
+ */
+public class IsChunkBreakpoint implements BreakpointPredicate {
+    private final int mLeadingZeros;
+    private final long mBitmask;
+
+    /**
+     * A new instance that causes a breakpoint after a given number of trials on average.
+     *
+     * @param averageNumberOfTrialsUntilBreakpoint The number of trials after which on average to
+     *     create a new chunk. If this is not a power of 2, some precision is sacrificed (i.e., on
+     *     average, breaks will actually happen after the nearest power of 2 to the average number
+     *     of trials passed in).
+     */
+    public IsChunkBreakpoint(long averageNumberOfTrialsUntilBreakpoint) {
+        checkArgument(
+                averageNumberOfTrialsUntilBreakpoint >= 0,
+                "Average number of trials must be non-negative");
+
+        // Want n leading zeros after t trials.
+        // P(leading zeros = n) = 1/2^n
+        // Expected num trials to get n leading zeros = 1/2^-n
+        // t = 1/2^-n
+        // n = log2(t)
+        mLeadingZeros = (int) Math.round(log2(averageNumberOfTrialsUntilBreakpoint));
+        mBitmask = ~(~0L >>> mLeadingZeros);
+    }
+
+    /**
+     * Returns {@code true} if {@code fingerprint} indicates that there should be a chunk
+     * breakpoint.
+     */
+    @Override
+    public boolean isBreakpoint(long fingerprint) {
+        return (fingerprint & mBitmask) == 0;
+    }
+
+    /** Returns the number of leading zeros in the fingerprint that causes a breakpoint. */
+    public int getLeadingZeros() {
+        return mLeadingZeros;
+    }
+
+    /**
+     * Calculates log base 2 of x. Not the most efficient possible implementation, but it's simple,
+     * obviously correct, and is only invoked on object construction.
+     */
+    private static double log2(double x) {
+        return Math.log(x) / Math.log(2);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
new file mode 100644
index 0000000..1e14ffa
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+/** Helper to calculate a 64-bit Rabin fingerprint over a 31-byte window. */
+public class RabinFingerprint64 {
+    private static final long DEFAULT_IRREDUCIBLE_POLYNOMIAL_64 = 0x000000000000001BL;
+    private static final int POLYNOMIAL_DEGREE = 64;
+    private static final int SLIDING_WINDOW_SIZE_BYTES = 31;
+
+    private final long mPoly64;
+    // Auxiliary tables to speed up the computation of Rabin fingerprints.
+    private final long[] mTableFP64 = new long[256];
+    private final long[] mTableOutByte = new long[256];
+
+    /**
+     * Constructs a new instance over the given irreducible 64-degree polynomial. It is up to the
+     * caller to determine that the polynomial is irreducible. If it is not the fingerprinting will
+     * not behave as expected.
+     *
+     * @param poly64 The polynomial.
+     */
+    public RabinFingerprint64(long poly64) {
+        mPoly64 = poly64;
+    }
+
+    /** Constructs a new instance using {@code x^64 + x^4 + x + 1} as the irreducible polynomial. */
+    public RabinFingerprint64() {
+        this(DEFAULT_IRREDUCIBLE_POLYNOMIAL_64);
+        computeFingerprintTables64();
+        computeFingerprintTables64Windowed();
+    }
+
+    /**
+     * Computes the fingerprint for the new sliding window given the fingerprint of the previous
+     * sliding window, the byte sliding in, and the byte sliding out.
+     *
+     * @param inChar The new char coming into the sliding window.
+     * @param outChar The left most char sliding out of the window.
+     * @param fingerPrint Fingerprint for previous window.
+     * @return New fingerprint for the new sliding window.
+     */
+    public long computeFingerprint64(byte inChar, byte outChar, long fingerPrint) {
+        return (fingerPrint << 8)
+                ^ (inChar & 0xFF)
+                ^ mTableFP64[(int) (fingerPrint >>> 56)]
+                ^ mTableOutByte[outChar & 0xFF];
+    }
+
+    /** Compute auxiliary tables to speed up the fingerprint computation. */
+    private void computeFingerprintTables64() {
+        long[] degreesRes64 = new long[POLYNOMIAL_DEGREE];
+        degreesRes64[0] = mPoly64;
+        for (int i = 1; i < POLYNOMIAL_DEGREE; i++) {
+            if ((degreesRes64[i - 1] & (1L << 63)) == 0) {
+                degreesRes64[i] = degreesRes64[i - 1] << 1;
+            } else {
+                degreesRes64[i] = (degreesRes64[i - 1] << 1) ^ mPoly64;
+            }
+        }
+        for (int i = 0; i < 256; i++) {
+            int currIndex = i;
+            for (int j = 0; (currIndex > 0) && (j < 8); j++) {
+                if ((currIndex & 0x1) == 1) {
+                    mTableFP64[i] ^= degreesRes64[j];
+                }
+                currIndex >>>= 1;
+            }
+        }
+    }
+
+    /**
+     * Compute auxiliary table {@code mTableOutByte} to facilitate the computing of fingerprints for
+     * sliding windows. This table is to take care of the effect on the fingerprint when the
+     * leftmost byte in the window slides out.
+     */
+    private void computeFingerprintTables64Windowed() {
+        // Auxiliary array degsRes64[8] defined by: <code>degsRes64[i] = x^(8 *
+        // SLIDING_WINDOW_SIZE_BYTES + i) mod this.mPoly64.</code>
+        long[] degsRes64 = new long[8];
+        degsRes64[0] = mPoly64;
+        for (int i = 65; i < 8 * (SLIDING_WINDOW_SIZE_BYTES + 1); i++) {
+            if ((degsRes64[(i - 1) % 8] & (1L << 63)) == 0) {
+                degsRes64[i % 8] = degsRes64[(i - 1) % 8] << 1;
+            } else {
+                degsRes64[i % 8] = (degsRes64[(i - 1) % 8] << 1) ^ mPoly64;
+            }
+        }
+        for (int i = 0; i < 256; i++) {
+            int currIndex = i;
+            for (int j = 0; (currIndex > 0) && (j < 8); j++) {
+                if ((currIndex & 0x1) == 1) {
+                    mTableOutByte[i] ^= degsRes64[j];
+                }
+                currIndex >>>= 1;
+            }
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 3a5232a..d6f2a87 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -260,6 +260,10 @@
     @Nullable private ParcelFileDescriptor mSavedState;
     @Nullable private ParcelFileDescriptor mBackupData;
     @Nullable private ParcelFileDescriptor mNewState;
+    // Indicates whether there was any data to be backed up, i.e. the queue was not empty
+    // and at least one of the packages had data. Used to avoid updating current token for
+    // empty backups.
+    private boolean mHasDataToBackup;
 
     /**
      * This {@link ConditionVariable} is used to signal that the cancel operation has been
@@ -332,6 +336,8 @@
     public void run() {
         Process.setThreadPriority(THREAD_PRIORITY);
 
+        mHasDataToBackup = false;
+
         int status = BackupTransport.TRANSPORT_OK;
         try {
             startTask();
@@ -529,10 +535,10 @@
 
         String callerLogString = "KVBT.finishTask()";
 
-        // If we succeeded and this is the first time we've done a backup, we can record the current
-        // backup dataset token.
+        // If the backup data was not empty, we succeeded and this is the first time
+        // we've done a backup, we can record the current backup dataset token.
         long currentToken = mBackupManagerService.getCurrentToken();
-        if ((status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
+        if (mHasDataToBackup && (status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
             try {
                 IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
                 mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
@@ -838,6 +844,8 @@
             return BackupTransport.TRANSPORT_OK;
         }
 
+        mHasDataToBackup = true;
+
         int status;
         try (ParcelFileDescriptor backupData =
                 ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 78e82b6..923ac00 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -186,8 +186,6 @@
     private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties
             .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
 
-    private static final String SHARED_SANDBOX_ID_PREFIX = "shared:";
-
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
 
@@ -1501,7 +1499,8 @@
     }
 
     private static String getSandboxId(String packageName, String sharedUserId) {
-        return sharedUserId == null ? packageName : SHARED_SANDBOX_ID_PREFIX + sharedUserId;
+        return sharedUserId == null
+                ? packageName : StorageManager.SHARED_SANDBOX_PREFIX + sharedUserId;
     }
 
     private void connect() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a33ac70..b5217ad 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -192,10 +192,10 @@
         public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
             synchronized (mAm) {
                 final ServiceMap smap = getServiceMapLocked(UserHandle.getUserId(uid));
-                final int N = smap.mServicesByName.size();
+                final int N = smap.mServicesByInstanceName.size();
                 final ArrayList<ServiceRecord> toStop = new ArrayList<>(N);
                 for (int i = 0; i < N; i++) {
-                    final ServiceRecord r = smap.mServicesByName.valueAt(i);
+                    final ServiceRecord r = smap.mServicesByInstanceName.valueAt(i);
                     if (uid == r.serviceInfo.applicationInfo.uid
                             || packageName.equals(r.serviceInfo.packageName)) {
                         if (r.isForeground) {
@@ -246,7 +246,7 @@
      */
     final class ServiceMap extends Handler {
         final int mUserId;
-        final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
+        final ArrayMap<ComponentName, ServiceRecord> mServicesByInstanceName = new ArrayMap<>();
         final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
 
         final ArrayList<ServiceRecord> mDelayedStartList = new ArrayList<>();
@@ -368,7 +368,7 @@
         // TODO: Deal with global services
         if (DEBUG_MU)
             Slog.v(TAG_MU, "getServiceByNameLocked(" + name + "), callingUser = " + callingUser);
-        return getServiceMapLocked(callingUser).mServicesByName.get(name);
+        return getServiceMapLocked(callingUser).mServicesByInstanceName.get(name);
     }
 
     boolean hasBackgroundServicesLocked(int callingUser) {
@@ -386,7 +386,7 @@
     }
 
     ArrayMap<ComponentName, ServiceRecord> getServicesLocked(int callingUser) {
-        return getServiceMapLocked(callingUser).mServicesByName;
+        return getServiceMapLocked(callingUser).mServicesByInstanceName;
     }
 
     private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
@@ -416,7 +416,7 @@
         }
 
         ServiceLookupResult res =
-            retrieveServiceLocked(service, resolvedType, callingPackage,
+            retrieveServiceLocked(service, null, resolvedType, callingPackage,
                     callingPid, callingUid, userId, true, callerFg, false, false);
         if (res == null) {
             return null;
@@ -444,7 +444,7 @@
         boolean forcedStandby = false;
         if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
             if (DEBUG_FOREGROUND_SERVICE) {
-                Slog.d(TAG, "Forcing bg-only service start only for " + r.shortName
+                Slog.d(TAG, "Forcing bg-only service start only for " + r.shortInstanceName
                         + " : bgLaunch=" + bgLaunch + " callerFg=" + callerFg);
             }
             forcedStandby = true;
@@ -464,7 +464,7 @@
                     // Not allowed, fall back to normal start service, failing siliently
                     // if background check restricts that.
                     Slog.w(TAG, "startForegroundService not allowed due to app op: service "
-                            + service + " to " + r.name.flattenToShortString()
+                            + service + " to " + r.shortInstanceName
                             + " from pid=" + callingPid + " uid=" + callingUid
                             + " pkg=" + callingPackage);
                     fgRequired = false;
@@ -484,7 +484,7 @@
                     r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
             if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
                 Slog.w(TAG, "Background start not allowed: service "
-                        + service + " to " + r.name.flattenToShortString()
+                        + service + " to " + r.shortInstanceName
                         + " from pid=" + callingPid + " uid=" + callingUid
                         + " pkg=" + callingPackage + " startFg?=" + fgRequired);
                 if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
@@ -741,7 +741,7 @@
         }
 
         // If this service is active, make sure it is stopped.
-        ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, null,
+        ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
                 Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false);
         if (r != null) {
             if (r.record != null) {
@@ -765,8 +765,8 @@
         ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
         ArrayList<ServiceRecord> stopping = null;
         if (services != null) {
-            for (int i=services.mServicesByName.size()-1; i>=0; i--) {
-                ServiceRecord service = services.mServicesByName.valueAt(i);
+            for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
+                ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
                 if (service.appInfo.uid == uid && service.startRequested) {
                     if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
                             service.appInfo.targetSdkVersion, -1, false, false, false)
@@ -774,7 +774,7 @@
                         if (stopping == null) {
                             stopping = new ArrayList<>();
                         }
-                        String compName = service.name.flattenToShortString();
+                        String compName = service.shortInstanceName;
                         EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
                         StringBuilder sb = new StringBuilder(64);
                         sb.append("Stopping service due to app idle: ");
@@ -801,7 +801,7 @@
     }
 
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
-        ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, callingPackage,
+        ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
                 Binder.getCallingPid(), Binder.getCallingUid(),
                 UserHandle.getCallingUserId(), false, false, false, false);
 
@@ -1228,7 +1228,7 @@
                     case AppOpsManager.MODE_IGNORED:
                         // Whoops, silently ignore this.
                         Slog.w(TAG, "Service.startForeground() not allowed due to app op: service "
-                                + r.shortName);
+                                + r.shortInstanceName);
                         ignoreForeground = true;
                         break;
                     default:
@@ -1239,7 +1239,7 @@
                         appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
                     Slog.w(TAG,
                             "Service.startForeground() not allowed due to bg restriction: service "
-                            + r.shortName);
+                            + r.shortInstanceName);
                     // Back off of any foreground expectations around this service, since we've
                     // just turned down its fg request.
                     updateServiceForegroundLocked(r.app, false);
@@ -1293,7 +1293,7 @@
                                 AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
                                 true);
                         StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-                                r.appInfo.uid, r.shortName,
+                                r.appInfo.uid, r.shortInstanceName,
                                 StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
                         mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
                     }
@@ -1343,7 +1343,7 @@
                         AppOpsManager.getToken(mAm.mAppOpsService),
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                 StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
-                        r.appInfo.uid, r.shortName,
+                        r.appInfo.uid, r.shortInstanceName,
                         StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
                 mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                 if (r.app != null) {
@@ -1373,8 +1373,8 @@
             // due the other service.
             ServiceMap sm = getServiceMapLocked(r.userId);
             if (sm != null) {
-                for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {
-                    ServiceRecord other = sm.mServicesByName.valueAt(i);
+                for (int i = sm.mServicesByInstanceName.size() - 1; i >= 0; i--) {
+                    ServiceRecord other = sm.mServicesByInstanceName.valueAt(i);
                     if (other != r && other.foregroundId == r.foregroundId
                             && other.packageName.equals(r.packageName)) {
                         // Found one!  Abort the cancel.
@@ -1466,7 +1466,8 @@
 
     int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
             String resolvedType, final IServiceConnection connection, int flags,
-            String callingPackage, final int userId) throws TransactionTooLargeException {
+            String instanceName, String callingPackage, final int userId)
+            throws TransactionTooLargeException {
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                 + " type=" + resolvedType + " conn=" + connection.asBinder()
                 + " flags=0x" + Integer.toHexString(flags));
@@ -1530,8 +1531,9 @@
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
 
         ServiceLookupResult res =
-            retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, true, callerFg, isBindExternal, allowInstant);
+            retrieveServiceLocked(service, instanceName, resolvedType, callingPackage,
+                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true,
+                    callerFg, isBindExternal, allowInstant);
         if (res == null) {
             return 0;
         }
@@ -1637,7 +1639,7 @@
 
             mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                     callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
-                    s.name, s.processName);
+                    s.instanceName, s.processName);
             // Once the apps have become associated, if one of them is caller is ephemeral
             // the target app should now be able to see the calling app
             mAm.grantEphemeralAccessLocked(callerApp.userId, service,
@@ -1709,7 +1711,7 @@
                 try {
                     c.conn.connected(s.name, b.intent.binder, false);
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failure sending service " + s.shortName
+                    Slog.w(TAG, "Failure sending service " + s.shortInstanceName
                             + " to connection " + c.conn.asBinder()
                             + " (in " + c.binding.client.processName + ")", e);
                 }
@@ -1763,9 +1765,9 @@
                             try {
                                 c.conn.connected(r.name, service, false);
                             } catch (Exception e) {
-                                Slog.w(TAG, "Failure sending service " + r.name +
-                                      " to connection " + c.conn.asBinder() +
-                                      " (in " + c.binding.client.processName + ")", e);
+                                Slog.w(TAG, "Failure sending service " + r.shortInstanceName
+                                      + " to connection " + c.conn.asBinder()
+                                      + " (in " + c.binding.client.processName + ")", e);
                             }
                         }
                     }
@@ -1898,7 +1900,8 @@
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
-            String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
+            String instanceName, String resolvedType, String callingPackage,
+            int callingPid, int callingUid, int userId,
             boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
             boolean allowInstant) {
         ServiceRecord r = null;
@@ -1909,12 +1912,23 @@
                 ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", null);
 
         ServiceMap smap = getServiceMapLocked(userId);
-        final ComponentName comp = service.getComponent();
+        final ComponentName comp;
+        if (instanceName == null) {
+            comp = service.getComponent();
+        } else {
+            final ComponentName realComp = service.getComponent();
+            if (realComp == null) {
+                throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
+                        + "' without expicit component in Intent");
+            }
+            comp = new ComponentName(realComp.getPackageName(),
+                    realComp.getClassName() + ":" + instanceName);
+        }
         if (comp != null) {
-            r = smap.mServicesByName.get(comp);
+            r = smap.mServicesByInstanceName.get(comp);
             if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by component: " + r);
         }
-        if (r == null && !isBindExternal) {
+        if (r == null && !isBindExternal && instanceName == null) {
             Intent.FilterComparison filter = new Intent.FilterComparison(service);
             r = smap.mServicesByIntent.get(filter);
             if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
@@ -1936,24 +1950,29 @@
                 // TODO: come back and remove this assumption to triage all services
                 ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,
                         resolvedType, flags, userId, callingUid);
-                ServiceInfo sInfo =
-                    rInfo != null ? rInfo.serviceInfo : null;
+                ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
                 if (sInfo == null) {
                     Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
                           ": not found");
                     return null;
                 }
-                ComponentName name = new ComponentName(
+                if (instanceName != null
+                        && (sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+                    throw new IllegalArgumentException("Can't use instance name '" + instanceName
+                            + "' with non-isolated service '" + sInfo.name + "'");
+                }
+                ComponentName className = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
+                ComponentName name = comp != null ? comp : className;
                 if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
                     if (isBindExternal) {
                         if (!sInfo.exported) {
-                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
-                                    " is not exported");
+                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+                                    + className + " is not exported");
                         }
                         if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
-                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
-                                    " is not an isolatedProcess");
+                            throw new SecurityException("BIND_EXTERNAL_SERVICE failed, "
+                                    + className + " is not an isolatedProcess");
                         }
                         // Run the service under the calling package's application.
                         ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
@@ -1967,6 +1986,9 @@
                         sInfo.applicationInfo.packageName = aInfo.packageName;
                         sInfo.applicationInfo.uid = aInfo.uid;
                         name = new ComponentName(aInfo.packageName, name.getClassName());
+                        className = new ComponentName(aInfo.packageName,
+                                instanceName == null ? className.getClassName()
+                                        : (className.getClassName() + ":" + instanceName));
                         service.setComponent(name);
                     } else {
                         throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
@@ -1986,7 +2008,7 @@
                     sInfo = new ServiceInfo(sInfo);
                     sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
                 }
-                r = smap.mServicesByName.get(name);
+                r = smap.mServicesByInstanceName.get(name);
                 if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE,
                         "Retrieved via pm by intent: " + r);
                 if (r == null && createIfNeeded) {
@@ -1997,19 +2019,20 @@
                     final BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
                     synchronized (stats) {
                         ss = stats.getServiceStatsLocked(
-                                sInfo.applicationInfo.uid, sInfo.packageName,
-                                sInfo.name);
+                                sInfo.applicationInfo.uid, name.getPackageName(),
+                                name.getClassName());
                     }
-                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
+                    r = new ServiceRecord(mAm, ss, className, name, filter, sInfo, callingFromFg,
+                            res);
                     res.setService(r);
-                    smap.mServicesByName.put(name, r);
+                    smap.mServicesByInstanceName.put(name, r);
                     smap.mServicesByIntent.put(filter, r);
 
                     // Make sure this component isn't in the pending list.
                     for (int i=mPendingServices.size()-1; i>=0; i--) {
                         final ServiceRecord pr = mPendingServices.get(i);
                         if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid
-                                && pr.name.equals(name)) {
+                                && pr.instanceName.equals(name)) {
                             if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Remove pending: " + pr);
                             mPendingServices.remove(i);
                         }
@@ -2024,14 +2047,14 @@
             if (mAm.checkComponentPermission(r.permission,
                     callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
                 if (!r.exported) {
-                    Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+                    Slog.w(TAG, "Permission Denial: Accessing service " + r.shortInstanceName
                             + " from pid=" + callingPid
                             + ", uid=" + callingUid
                             + " that is not exported from uid " + r.appInfo.uid);
                     return new ServiceLookupResult(null, "not exported from uid "
                             + r.appInfo.uid);
                 }
-                Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+                Slog.w(TAG, "Permission Denial: Accessing service " + r.shortInstanceName
                         + " from pid=" + callingPid
                         + ", uid=" + callingUid
                         + " requires " + r.permission);
@@ -2040,7 +2063,7 @@
                 final int opCode = AppOpsManager.permissionToOpCode(r.permission);
                 if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.noteOperation(
                         opCode, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                    Slog.w(TAG, "Appop Denial: Accessing service " + r.name
+                    Slog.w(TAG, "Appop Denial: Accessing service " + r.shortInstanceName
                             + " from pid=" + callingPid
                             + ", uid=" + callingUid
                             + " requires appop " + AppOpsManager.opToName(opCode));
@@ -2061,7 +2084,7 @@
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
                 + why + " of " + r + " in app " + r.app);
         else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
-                + why + " of " + r.shortName);
+                + why + " of " + r.shortInstanceName);
 
         // For b/34123235: Services within the system server won't start until SystemServer
         // does Looper.loop(), so we shouldn't try to start/bind to them too early in the boot
@@ -2146,14 +2169,14 @@
         boolean canceled = false;
 
         if (mAm.mAtmInternal.isShuttingDown()) {
-            Slog.w(TAG, "Not scheduling restart of crashed service " + r.shortName
+            Slog.w(TAG, "Not scheduling restart of crashed service " + r.shortInstanceName
                     + " - system is shutting down");
             return false;
         }
 
         ServiceMap smap = getServiceMapLocked(r.userId);
-        if (smap.mServicesByName.get(r.name) != r) {
-            ServiceRecord cur = smap.mServicesByName.get(r.name);
+        if (smap.mServicesByInstanceName.get(r.instanceName) != r) {
+            ServiceRecord cur = smap.mServicesByInstanceName.get(r.instanceName);
             Slog.wtf(TAG, "Attempting to schedule restart of " + r
                     + " when found in map: " + cur);
             return false;
@@ -2184,7 +2207,7 @@
                         if (resetTime < dur) resetTime = dur;
                     } else {
                         Slog.w(TAG, "Canceling start item " + si.intent + " in service "
-                                + r.name);
+                                + r.shortInstanceName);
                         canceled = true;
                     }
                 }
@@ -2256,9 +2279,9 @@
         mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
         r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
         Slog.w(TAG, "Scheduling restart of crashed service "
-                + r.shortName + " in " + r.restartDelay + "ms");
+                + r.shortInstanceName + " in " + r.restartDelay + "ms");
         EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
-                r.userId, r.shortName, r.restartDelay);
+                r.userId, r.shortInstanceName, r.restartDelay);
 
         return canceled;
     }
@@ -2393,7 +2416,7 @@
                 } catch (TransactionTooLargeException e) {
                     throw e;
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
+                    Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
                 }
 
                 // If a dead object exception was thrown -- fall through to
@@ -2417,7 +2440,7 @@
         // to be executed when the app comes up.
         if (app == null && !permissionsReviewRequired) {
             if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
-                    hostingType, r.name, false, isolated, false)) == null) {
+                    hostingType, r.instanceName, false, isolated, false)) == null) {
                 String msg = "Unable to launch app "
                         + r.appInfo.packageName + "/"
                         + r.appInfo.uid + " for service "
@@ -2488,8 +2511,9 @@
         try {
             if (LOG_SERVICE_START_STOP) {
                 String nameTerm;
-                int lastPeriod = r.shortName.lastIndexOf('.');
-                nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
+                int lastPeriod = r.shortInstanceName.lastIndexOf('.');
+                nameTerm = lastPeriod >= 0 ? r.shortInstanceName.substring(lastPeriod)
+                        : r.shortInstanceName;
                 EventLogTags.writeAmCreateService(
                         r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
             }
@@ -2705,9 +2729,9 @@
                 try {
                     cr.conn.connected(r.name, null, true);
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failure disconnecting service " + r.name +
-                          " to connection " + c.get(i).conn.asBinder() +
-                          " (in " + c.get(i).binding.client.processName + ")", e);
+                    Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
+                          + " to connection " + c.get(i).conn.asBinder()
+                          + " (in " + c.get(i).binding.client.processName + ")", e);
                 }
             }
         }
@@ -2728,7 +2752,7 @@
                                 ibr.intent.getIntent());
                     } catch (Exception e) {
                         Slog.w(TAG, "Exception when unbinding service "
-                                + r.shortName, e);
+                                + r.shortInstanceName, e);
                         serviceProcessGoneLocked(r);
                     }
                 }
@@ -2773,14 +2797,14 @@
         }
 
         final ServiceMap smap = getServiceMapLocked(r.userId);
-        ServiceRecord found = smap.mServicesByName.remove(r.name);
+        ServiceRecord found = smap.mServicesByInstanceName.remove(r.instanceName);
 
         // Note when this method is called by bringUpServiceLocked(), the service is not found
-        // in mServicesByName and found will be null.
+        // in mServicesByInstanceName and found will be null.
         if (found != null && found != r) {
             // This is not actually the service we think is running...  this should not happen,
             // but if it does, fail hard.
-            smap.mServicesByName.put(r.name, found);
+            smap.mServicesByInstanceName.put(r.instanceName, found);
             throw new IllegalStateException("Bringing down " + r + " but actually running "
                     + found);
         }
@@ -2807,8 +2831,8 @@
             mAm.mAppOpsService.finishOperation(
                     AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
-            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
-                    StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+            StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid,
+                    r.shortInstanceName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
             mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
         }
 
@@ -2838,7 +2862,7 @@
                     r.app.thread.scheduleStopService(r);
                 } catch (Exception e) {
                     Slog.w(TAG, "Exception when destroying service "
-                            + r.shortName, e);
+                            + r.shortInstanceName, e);
                     serviceProcessGoneLocked(r);
                 }
             } else {
@@ -2915,7 +2939,7 @@
         }
 
         mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid,
-                s.appInfo.longVersionCode, s.name, s.processName);
+                s.appInfo.longVersionCode, s.instanceName, s.processName);
 
         if (b.connections.size() == 0) {
             b.intent.apps.remove(b.client);
@@ -2942,7 +2966,7 @@
                     b.intent.doRebind = false;
                     s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
                 } catch (Exception e) {
-                    Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
+                    Slog.w(TAG, "Exception when unbinding service " + s.shortInstanceName, e);
                     serviceProcessGoneLocked(s);
                 }
             }
@@ -3061,17 +3085,17 @@
                 + ": nesting=" + r.executeNesting
                 + ", inDestroying=" + inDestroying + ", app=" + r.app);
         else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
-                "<<< DONE EXECUTING " + r.shortName);
+                "<<< DONE EXECUTING " + r.shortInstanceName);
         r.executeNesting--;
         if (r.executeNesting <= 0) {
             if (r.app != null) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
-                        "Nesting at 0 of " + r.shortName);
+                        "Nesting at 0 of " + r.shortInstanceName);
                 r.app.execServicesFg = false;
                 r.app.executingServices.remove(r);
                 if (r.app.executingServices.size() == 0) {
                     if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
-                            "No more executingServices of " + r.shortName);
+                            "No more executingServices of " + r.shortInstanceName);
                     mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
                 } else if (r.executeFg) {
                     // Need to re-evaluate whether the app still needs to be in the foreground.
@@ -3141,7 +3165,7 @@
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception in new application when starting service "
-                        + sr.shortName, e);
+                        + sr.shortInstanceName, e);
                 throw e;
             }
         }
@@ -3227,7 +3251,8 @@
         if (userId == UserHandle.USER_ALL) {
             for (int i = mServiceMap.size() - 1; i >= 0; i--) {
                 didSomething |= collectPackageServicesLocked(packageName, filterByClasses,
-                        evenPersistent, doit, killProcess, mServiceMap.valueAt(i).mServicesByName);
+                        evenPersistent, doit, killProcess,
+                        mServiceMap.valueAt(i).mServicesByInstanceName);
                 if (!doit && didSomething) {
                     return true;
                 }
@@ -3238,7 +3263,7 @@
         } else {
             ServiceMap smap = mServiceMap.get(userId);
             if (smap != null) {
-                ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByName;
+                ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByInstanceName;
                 didSomething = collectPackageServicesLocked(packageName, filterByClasses,
                         evenPersistent, doit, killProcess, items);
             }
@@ -3288,7 +3313,7 @@
             ServiceRecord sr = services.get(i);
             if (sr.startRequested) {
                 if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
-                    Slog.i(TAG, "Stopping service " + sr.shortName + ": remove task");
+                    Slog.i(TAG, "Stopping service " + sr.shortInstanceName + ": remove task");
                     stopServiceLocked(sr);
                 } else {
                     sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
@@ -3326,7 +3351,7 @@
                                 } catch (Exception e) {
                                     // todo: this should be asynchronous!
                                     Slog.w(TAG, "Exception thrown disconnected servce "
-                                          + r.shortName
+                                          + r.shortInstanceName
                                           + " from app " + app.processName, e);
                                 }
                             }
@@ -3400,7 +3425,7 @@
                     if (false && proc != null && !proc.isPersistent() && proc.thread != null
                             && proc.pid != 0 && proc.pid != ActivityManagerService.MY_PID
                             && proc.setProcState >= ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
-                        proc.kill("bound to service " + sr.name.flattenToShortString()
+                        proc.kill("bound to service " + sr.shortInstanceName
                                 + " in dying proc " + (app != null ? app.processName : "??"), true);
                     }
                 }
@@ -3421,7 +3446,7 @@
 
             // Sanity check: if the service listed for the app is not one
             // we actually are maintaining, just let it drop.
-            final ServiceRecord curRec = smap.mServicesByName.get(sr.name);
+            final ServiceRecord curRec = smap.mServicesByInstanceName.get(sr.instanceName);
             if (curRec != sr) {
                 if (curRec != null) {
                     Slog.wtf(TAG, "Service " + sr + " in process " + app
@@ -3438,7 +3463,7 @@
                 Slog.w(TAG, "Service crashed " + sr.crashCount
                         + " times, stopping: " + sr);
                 EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
-                        sr.userId, sr.crashCount, sr.shortName, app.pid);
+                        sr.userId, sr.crashCount, sr.shortInstanceName, app.pid);
                 bringDownServiceLocked(sr);
             } else if (!allowRestart
                     || !mAm.mUserController.isUserRunning(sr.userId, 0)) {
@@ -3644,7 +3669,7 @@
                 mLastAnrDump = sw.toString();
                 mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                 mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
-                anrMessage = "executing service " + timeout.shortName;
+                anrMessage = "executing service " + timeout.shortInstanceName;
             } else {
                 Message msg = mAm.mHandler.obtainMessage(
                         ActivityManagerService.SERVICE_TIMEOUT_MSG);
@@ -3690,7 +3715,8 @@
         final int userId = UserHandle.getUserId(applicationInfo.uid);
         ServiceMap serviceMap = mServiceMap.get(userId);
         if (serviceMap != null) {
-            ArrayMap<ComponentName, ServiceRecord> servicesByName = serviceMap.mServicesByName;
+            ArrayMap<ComponentName, ServiceRecord> servicesByName
+                    = serviceMap.mServicesByInstanceName;
             for (int j = servicesByName.size() - 1; j >= 0; j--) {
                 ServiceRecord serviceRecord = servicesByName.valueAt(j);
                 if (applicationInfo.packageName.equals(serviceRecord.appInfo.packageName)) {
@@ -3761,9 +3787,9 @@
             final int[] users = mAm.mUserController.getUsers();
             for (int user : users) {
                 ServiceMap smap = getServiceMapLocked(user);
-                if (smap.mServicesByName.size() > 0) {
-                    for (int si=0; si<smap.mServicesByName.size(); si++) {
-                        ServiceRecord r = smap.mServicesByName.valueAt(si);
+                if (smap.mServicesByInstanceName.size() > 0) {
+                    for (int si=0; si<smap.mServicesByInstanceName.size(); si++) {
+                        ServiceRecord r = smap.mServicesByInstanceName.valueAt(si);
                         if (!matcher.match(r, r.name)) {
                             continue;
                         }
@@ -4164,7 +4190,7 @@
                 }
                 long token = proto.start(ActiveServicesProto.SERVICES_BY_USERS);
                 proto.write(ActiveServicesProto.ServicesByUser.USER_ID, user);
-                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByInstanceName;
                 for (int i=0; i<alls.size(); i++) {
                     alls.valueAt(i).writeToProto(proto,
                             ActiveServicesProto.ServicesByUser.SERVICE_RECORDS);
@@ -4197,7 +4223,7 @@
                 if (smap == null) {
                     continue;
                 }
-                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByInstanceName;
                 for (int i=0; i<alls.size(); i++) {
                     ServiceRecord r1 = alls.valueAt(i);
 
@@ -4235,7 +4261,7 @@
         String innerPrefix = prefix + "  ";
         synchronized (mAm) {
             pw.print(prefix); pw.print("SERVICE ");
-                    pw.print(r.shortName); pw.print(" ");
+                    pw.print(r.shortInstanceName); pw.print(" ");
                     pw.print(Integer.toHexString(System.identityHashCode(r)));
                     pw.print(" pid=");
                     if (r.app != null) pw.println(r.app.pid);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0bae1f3..771d376 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13183,8 +13183,15 @@
     }
 
     public int bindService(IApplicationThread caller, IBinder token, Intent service,
-            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
-            int userId) throws TransactionTooLargeException {
+            String resolvedType, IServiceConnection connection, int flags,
+            String callingPackage, int userId) throws TransactionTooLargeException {
+        return bindIsolatedService(caller, token, service, resolvedType, connection, flags,
+                null, callingPackage, userId);
+    }
+
+    public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
+            String resolvedType, IServiceConnection connection, int flags, String instanceName,
+            String callingPackage, int userId) throws TransactionTooLargeException {
         enforceNotIsolatedCaller("bindService");
 
         // Refuse possible leaked file descriptors
@@ -13198,7 +13205,7 @@
 
         synchronized(this) {
             return mServices.bindServiceLocked(caller, token, service,
-                    resolvedType, connection, flags, callingPackage, userId);
+                    resolvedType, connection, flags, instanceName, callingPackage, userId);
         }
     }
 
@@ -15831,7 +15838,7 @@
                                     mayBeTop = true;
                                     mayBeTopType = "service";
                                     mayBeTopSource = cr.binding.client;
-                                    mayBeTopTarget = s.name;
+                                    mayBeTopTarget = s.instanceName;
                                     clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
                                 } else {
                                     // Special handling for above-top states (persistent
@@ -15885,7 +15892,7 @@
                                     .REASON_SERVICE_IN_USE;
                             app.adjSource = cr.binding.client;
                             app.adjSourceProcState = clientProcState;
-                            app.adjTarget = s.name;
+                            app.adjTarget = s.instanceName;
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
                                         + ": " + app + ", due to " + cr.binding.client
@@ -15915,7 +15922,7 @@
                                     .REASON_SERVICE_IN_USE;
                             app.adjSource = a;
                             app.adjSourceProcState = procState;
-                            app.adjTarget = s.name;
+                            app.adjTarget = s.instanceName;
                             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                         "Raise to service w/activity: " + app);
@@ -19170,6 +19177,9 @@
             if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
                 return false;
             }
+            if (uid == SHELL_UID || uid == ROOT_UID) {
+                return false;
+            }
             synchronized (mPidsSelfLocked) {
                 final ProcessRecord pr = mPidsSelfLocked.get(pid);
                 return pr == null || pr.mountMode != Zygote.MOUNT_EXTERNAL_FULL;
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index 4eaebd0..9870420 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -59,12 +59,12 @@
     public String toString() {
         return "AppBindRecord{"
             + Integer.toHexString(System.identityHashCode(this))
-            + " " + service.shortName + ":" + client.processName + "}";
+            + " " + service.shortInstanceName + ":" + client.processName + "}";
     }
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(AppBindRecordProto.SERVICE_NAME, service.shortName);
+        proto.write(AppBindRecordProto.SERVICE_NAME, service.shortInstanceName);
         proto.write(AppBindRecordProto.CLIENT_PROC_NAME, client.processName);
         final int N = connections.size();
         for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 37d07bb..bfa3f66 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -115,16 +115,16 @@
                 && (binding.service.appInfo.uid != clientUid
                         || !binding.service.processName.equals(clientProcessName))) {
             ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get(
-                    binding.service.name.getPackageName());
+                    binding.service.instanceName.getPackageName());
             if (holder == null) {
                 Slog.wtf(TAG_AM, "No package in referenced service "
-                        + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+                        + binding.service.shortInstanceName + ": proc=" + binding.service.app);
             } else if (holder.pkg == null) {
                 Slog.wtf(TAG_AM, "Inactive holder in referenced service "
-                        + binding.service.name.toShortString() + ": proc=" + binding.service.app);
+                        + binding.service.shortInstanceName + ": proc=" + binding.service.app);
             } else {
                 association = holder.pkg.getAssociationStateLocked(holder.state,
-                        binding.service.name.getClassName()).startSource(clientUid,
+                        binding.service.instanceName.getClassName()).startSource(clientUid,
                         clientProcessName);
 
             }
@@ -202,7 +202,7 @@
         if (serviceDead) {
             sb.append("DEAD ");
         }
-        sb.append(binding.service.shortName);
+        sb.append(binding.service.shortInstanceName);
         sb.append(":@");
         sb.append(Integer.toHexString(System.identityHashCode(conn.asBinder())));
         sb.append('}');
@@ -223,7 +223,7 @@
             proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
         }
         if (binding.service != null) {
-            proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
+            proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortInstanceName);
         }
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index 839b6e1..90aef3e 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -99,7 +99,7 @@
         if ((collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
             sb.append("CR ");
         }
-        sb.append(service.shortName);
+        sb.append(service.shortInstanceName);
         sb.append(':');
         if (intent != null) {
             intent.getIntent().toShortString(sb, false, false, false, false);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index d8f94c9..09f8c3e 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -70,7 +70,8 @@
     final ActivityManagerService ams;
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
-    final String shortName; // name.flattenToShortString().
+    final ComponentName instanceName; // service component's per-instance name.
+    final String shortInstanceName; // instanceName.flattenToShortString().
     final Intent.FilterComparison intent;
                             // original intent used to find service.
     final ServiceInfo serviceInfo;
@@ -190,7 +191,7 @@
             StringBuilder sb = new StringBuilder(128);
             sb.append("ServiceRecord{")
                 .append(Integer.toHexString(System.identityHashCode(sr)))
-                .append(' ').append(sr.shortName)
+                .append(' ').append(sr.shortInstanceName)
                 .append(" StartItem ")
                 .append(Integer.toHexString(System.identityHashCode(this)))
                 .append(" id=").append(id).append('}');
@@ -235,7 +236,7 @@
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
+        proto.write(ServiceRecordProto.SHORT_NAME, this.shortInstanceName);
         proto.write(ServiceRecordProto.IS_RUNNING, app != null);
         if (app != null) {
             proto.write(ServiceRecordProto.PID, app.pid);
@@ -448,12 +449,14 @@
 
     ServiceRecord(ActivityManagerService ams,
             BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
+            ComponentName instanceName,
             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
             Runnable restarter) {
         this.ams = ams;
         this.stats = servStats;
         this.name = name;
-        shortName = name.flattenToShortString();
+        this.instanceName = instanceName;
+        shortInstanceName = instanceName.flattenToShortString();
         this.intent = intent;
         serviceInfo = sInfo;
         appInfo = sInfo.applicationInfo;
@@ -618,7 +621,7 @@
                             // those dirty apps we will create a notification clearly
                             // blaming the app.
                             Slog.v(TAG, "Attempted to start a foreground service ("
-                                    + name
+                                    + shortInstanceName
                                     + ") with a broken notification (no icon: "
                                     + localForegroundNoti
                                     + ")");
@@ -701,7 +704,7 @@
                         Slog.w(TAG, "Error showing notification for service", e);
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
-                        ams.setServiceForeground(name, ServiceRecord.this,
+                        ams.setServiceForeground(instanceName, ServiceRecord.this,
                                 0, null, 0);
                         ams.crashApplication(appUid, appPid, localPackageName, -1,
                                 "Bad notification for startForeground: " + e);
@@ -773,7 +776,7 @@
         sb.append("ServiceRecord{")
             .append(Integer.toHexString(System.identityHashCode(this)))
             .append(" u").append(userId)
-            .append(' ').append(shortName).append('}');
+            .append(' ').append(shortInstanceName).append('}');
         return stringName = sb.toString();
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b404c41..1c7572e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3610,7 +3610,7 @@
                         NotificationRecord r = mNotificationsByKey.get(adjustment.getKey());
                         if (r != null && mAssistants.isSameUser(token, r.getUserId())) {
                             applyAdjustment(r, adjustment);
-                            r.applyAdjustments();
+                            r.applyImportanceFromAdjustments();
                             if (r.getImportance() == IMPORTANCE_NONE) {
                                 cancelNotificationsFromListener(token, new String[]{r.getKey()});
                             } else {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 84d0c01..a11b03f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -664,6 +664,18 @@
                             .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
                                     getSmartReplies().size()));
                 }
+            }
+            applyImportanceFromAdjustments();
+        }
+    }
+
+    /**
+     * Update importance from the adjustment.
+     */
+    public void applyImportanceFromAdjustments() {
+        synchronized (mAdjustments) {
+            for (Adjustment adjustment : mAdjustments) {
+                Bundle signals = adjustment.getSignals();
                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 26f6e96..ea190a7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1126,101 +1126,108 @@
             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
         }
 
-        if (!params.isMultiPackage) {
-            Preconditions.checkNotNull(mPackageName);
-            Preconditions.checkNotNull(mSigningDetails);
-            Preconditions.checkNotNull(mResolvedBaseFile);
+        final IPackageInstallObserver2 localObserver;
+        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+            localObserver = null;
+        } else {
+            if (!params.isMultiPackage) {
+                Preconditions.checkNotNull(mPackageName);
+                Preconditions.checkNotNull(mSigningDetails);
+                Preconditions.checkNotNull(mResolvedBaseFile);
 
-            if (needToAskForPermissionsLocked()) {
-                // User needs to confirm installation;
-                // give installer an intent they can use to involve
-                // user.
-                final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
-                intent.setPackage(mPm.getPackageInstallerPackageName());
-                intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-                try {
-                    mRemoteObserver.onUserActionRequired(intent);
-                } catch (RemoteException ignored) {
-                }
-
-                // Commit was keeping session marked as active until now; release
-                // that extra refcount so session appears idle.
-                closeInternal(false);
-                return null;
-            }
-
-            // Inherit any packages and native libraries from existing install that
-            // haven't been overridden.
-            if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
-                try {
-                    final List<File> fromFiles = mResolvedInheritedFiles;
-                    final File toDir = resolveStageDirLocked();
-
-                    if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
-                    if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
-                        throw new IllegalStateException("mInheritedFilesBase == null");
+                if (needToAskForPermissionsLocked()) {
+                    // User needs to confirm installation;
+                    // give installer an intent they can use to involve
+                    // user.
+                    final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
+                    intent.setPackage(mPm.getPackageInstallerPackageName());
+                    intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+                    try {
+                        mRemoteObserver.onUserActionRequired(intent);
+                    } catch (RemoteException ignored) {
                     }
 
-                    if (isLinkPossible(fromFiles, toDir)) {
-                        if (!mResolvedInstructionSets.isEmpty()) {
-                            final File oatDir = new File(toDir, "oat");
-                            createOatDirs(mResolvedInstructionSets, oatDir);
+                    // Commit was keeping session marked as active until now; release
+                    // that extra refcount so session appears idle.
+                    closeInternal(false);
+                    return null;
+                }
+
+                // Inherit any packages and native libraries from existing install that
+                // haven't been overridden.
+                if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+                    try {
+                        final List<File> fromFiles = mResolvedInheritedFiles;
+                        final File toDir = resolveStageDirLocked();
+
+                        if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+                        if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+                            throw new IllegalStateException("mInheritedFilesBase == null");
                         }
-                        // pre-create lib dirs for linking if necessary
-                        if (!mResolvedNativeLibPaths.isEmpty()) {
-                            for (String libPath : mResolvedNativeLibPaths) {
-                                // "/lib/arm64" -> ["lib", "arm64"]
-                                final int splitIndex = libPath.lastIndexOf('/');
-                                if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
-                                    Slog.e(TAG,
-                                            "Skipping native library creation for linking due to "
-                                                    + "invalid path: " + libPath);
-                                    continue;
-                                }
-                                final String libDirPath = libPath.substring(1, splitIndex);
-                                final File libDir = new File(toDir, libDirPath);
-                                if (!libDir.exists()) {
-                                    NativeLibraryHelper.createNativeLibrarySubdir(libDir);
-                                }
-                                final String archDirPath = libPath.substring(splitIndex + 1);
-                                NativeLibraryHelper.createNativeLibrarySubdir(
-                                        new File(libDir, archDirPath));
+
+                        if (isLinkPossible(fromFiles, toDir)) {
+                            if (!mResolvedInstructionSets.isEmpty()) {
+                                final File oatDir = new File(toDir, "oat");
+                                createOatDirs(mResolvedInstructionSets, oatDir);
                             }
+                            // pre-create lib dirs for linking if necessary
+                            if (!mResolvedNativeLibPaths.isEmpty()) {
+                                for (String libPath : mResolvedNativeLibPaths) {
+                                    // "/lib/arm64" -> ["lib", "arm64"]
+                                    final int splitIndex = libPath.lastIndexOf('/');
+                                    if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+                                        Slog.e(TAG,
+                                                "Skipping native library creation for linking due"
+                                                        + " to invalid path: " + libPath);
+                                        continue;
+                                    }
+                                    final String libDirPath = libPath.substring(1, splitIndex);
+                                    final File libDir = new File(toDir, libDirPath);
+                                    if (!libDir.exists()) {
+                                        NativeLibraryHelper.createNativeLibrarySubdir(libDir);
+                                    }
+                                    final String archDirPath = libPath.substring(splitIndex + 1);
+                                    NativeLibraryHelper.createNativeLibrarySubdir(
+                                            new File(libDir, archDirPath));
+                                }
+                            }
+                            linkFiles(fromFiles, toDir, mInheritedFilesBase);
+                        } else {
+                            // TODO: this should delegate to DCS so the system process
+                            // avoids holding open FDs into containers.
+                            copyFiles(fromFiles, toDir);
                         }
-                        linkFiles(fromFiles, toDir, mInheritedFilesBase);
-                    } else {
-                        // TODO: this should delegate to DCS so the system process
-                        // avoids holding open FDs into containers.
-                        copyFiles(fromFiles, toDir);
+                    } catch (IOException e) {
+                        throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                                "Failed to inherit existing install", e);
                     }
-                } catch (IOException e) {
-                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
-                            "Failed to inherit existing install", e);
                 }
+
+                // TODO: surface more granular state from dexopt
+                mInternalProgress = 0.5f;
+                computeProgressLocked(true);
+
+                // Unpack native libraries
+                extractNativeLibraries(mResolvedStageDir, params.abiOverride,
+                        mayInheritNativeLibs());
             }
 
-            // TODO: surface more granular state from dexopt
-            mInternalProgress = 0.5f;
-            computeProgressLocked(true);
+            // We've reached point of no return; call into PMS to install the stage.
+            // Regardless of success or failure we always destroy session.
+            localObserver = new IPackageInstallObserver2.Stub() {
+                @Override
+                public void onUserActionRequired(Intent intent) {
+                    throw new IllegalStateException();
+                }
 
-            // Unpack native libraries
-            extractNativeLibraries(mResolvedStageDir, params.abiOverride, mayInheritNativeLibs());
+                @Override
+                public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+                        Bundle extras) {
+                    destroyInternal();
+                    dispatchSessionFinished(returnCode, msg, extras);
+                }
+            };
         }
-        // We've reached point of no return; call into PMS to install the stage.
-        // Regardless of success or failure we always destroy session.
-        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
-            @Override
-            public void onUserActionRequired(Intent intent) {
-                throw new IllegalStateException();
-            }
-
-            @Override
-            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
-                    Bundle extras) {
-                destroyInternal();
-                dispatchSessionFinished(returnCode, msg, extras);
-            }
-        };
 
         final UserHandle user;
         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
@@ -1230,11 +1237,9 @@
         }
 
         mRelinquished = true;
-        final PackageManagerService.ActiveInstallSession activeInstallSession =
-                new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
-                        localObserver, params, mInstallerPackageName, mInstallerUid, user,
-                        mSigningDetails);
-        return activeInstallSession;
+        return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
+                localObserver, params, mInstallerPackageName, mInstallerUid, user,
+                mSigningDetails);
     }
 
     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 72d3406..8a0c416 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9308,7 +9308,7 @@
         }
     }
 
-    private SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+    private @Nullable SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return null;
@@ -9580,16 +9580,26 @@
     private void applyDefiningSharedLibraryUpdateLocked(
             PackageParser.Package pkg, SharedLibraryInfo libInfo,
             BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
+        // Note that libraries defined by this package may be null if:
+        // - Package manager was unable to create the shared library. The package still
+        //   gets installed, but the shared library does not get created.
+        // Or:
+        // - Package manager is in a state where package isn't scanned yet. This will
+        //   get called again after scanning to fix the dependencies.
         if (pkg.isLibrary()) {
             if (pkg.staticSharedLibName != null) {
                 SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
                         pkg.staticSharedLibName, pkg.staticSharedLibVersion);
-                action.accept(definedLibrary, libInfo);
+                if (definedLibrary != null) {
+                    action.accept(definedLibrary, libInfo);
+                }
             } else {
                 for (String libraryName : pkg.libraryNames) {
                     SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
                             libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
-                    action.accept(definedLibrary, libInfo);
+                    if (definedLibrary != null) {
+                        action.accept(definedLibrary, libInfo);
+                    }
                 }
             }
         }
@@ -17763,30 +17773,58 @@
         return true;
     }
 
+    private static class DeletePackageAction {
+        public final PackageSetting deletingPs;
+
+        private DeletePackageAction(PackageSetting deletingPs) {
+            this.deletingPs = deletingPs;
+        }
+    }
+
+    /**
+     * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null}
+     * otherwise.
+     */
+    @Nullable
+    private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) {
+        synchronized (mPackages) {
+            final PackageSetting ps;
+            ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                return null;
+            }
+            return new DeletePackageAction(ps);
+        }
+    }
+
     /*
      * This method handles package deletion in general
      */
-    private boolean deletePackageLIF(String packageName, UserHandle user,
+    private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
             boolean deleteCodeAndResources, int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
-        if (packageName == null) {
-            Slog.w(TAG, "Attempt to delete null packageName.");
+        final DeletePackageAction action = mayDeletePackageLIF(packageName);
+        if (null == action) {
             return false;
         }
 
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
 
-        PackageSetting ps;
-        synchronized (mPackages) {
-            ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
-                return false;
-            }
+        return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
+                allUserHandles, flags, outInfo, writeSettings, replacingPackage);
+    }
 
-            if (ps.parentPackageName != null && (!isSystemApp(ps)
-                    || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
+    private boolean executeDeletePackageLIF(DeletePackageAction action,
+            String packageName, UserHandle user, boolean deleteCodeAndResources,
+            int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
+            boolean writeSettings, PackageParser.Package replacingPackage) {
+        final PackageSetting ps = action.deletingPs;
+        final boolean systemApp = isSystemApp(ps);
+        synchronized (mPackages) {
+
+            if (ps.parentPackageName != null
+                    && (!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
                 if (DEBUG_REMOVE) {
                     Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
                             + ((user == null) ? UserHandle.USER_ALL : user));
@@ -17794,9 +17832,7 @@
                 final int removedUserId = (user != null) ? user.getIdentifier()
                         : UserHandle.USER_ALL;
 
-                if (!clearPackageStateForUserLIF(ps, removedUserId, outInfo)) {
-                    return false;
-                }
+                clearPackageStateForUserLIF(ps, removedUserId, outInfo);
                 markPackageUninstalledForUserLPw(ps, user);
                 scheduleWritePackageRestrictionsLocked(user);
                 return true;
@@ -17809,7 +17845,7 @@
         }
 
 
-        if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+        if (((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
                 && user.getIdentifier() != UserHandle.USER_ALL)) {
             // The caller is asking that the package only be deleted for a single
             // user.  To do this, we just mark its uninstalled state and delete
@@ -17818,7 +17854,7 @@
             // semantics than normal for uninstalling system apps.
             markPackageUninstalledForUserLPw(ps, user);
 
-            if (!isSystemApp(ps)) {
+            if (!systemApp) {
                 // Do not uninstall the APK if an app should be cached
                 boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
                 if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
@@ -17826,9 +17862,7 @@
                     // we need to do is clear this user's data and save that
                     // it is uninstalled.
                     if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
-                    if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {
-                        return false;
-                    }
+                    clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                     scheduleWritePackageRestrictionsLocked(user);
                     return true;
                 } else {
@@ -17844,9 +17878,7 @@
                 // we need to do is clear this user's data and save that
                 // it is uninstalled.
                 if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
-                if (!clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo)) {
-                    return false;
-                }
+                clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                 scheduleWritePackageRestrictionsLocked(user);
                 return true;
             }
@@ -17872,8 +17904,9 @@
             }
         }
 
-        boolean ret = false;
-        if (isSystemApp(ps)) {
+        // TODO(b/109941548): break reasons for ret = false out into mayDelete method
+        final boolean ret;
+        if (systemApp) {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
@@ -17902,7 +17935,7 @@
             // If we uninstalled an update to a system app there may be some
             // child packages that appeared as they are declared in the system
             // app but were not declared in the update.
-            if (isSystemApp(ps)) {
+            if (systemApp) {
                 synchronized (mPackages) {
                     PackageSetting updatedPs = mSettings.getPackageLPr(ps.name);
                     final int childCount = (updatedPs.childPackageNames != null)
@@ -17963,7 +17996,7 @@
         mSettings.writeKernelMappingLPr(ps);
     }
 
-    private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,
+    private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
             PackageRemovedInfo outInfo) {
         final PackageParser.Package pkg;
         synchronized (mPackages) {
@@ -17999,8 +18032,6 @@
             outInfo.removedUsers = userIds;
             outInfo.broadcastUsers = userIds;
         }
-
-        return true;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4c93441..6009bd3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2637,7 +2637,7 @@
         }
 
         for (final SharedUserSetting sus : mSharedUsers.values()) {
-            knownSet.remove(sus.getSandboxName());
+            knownSet.remove(sus.getStorageSandboxName());
         }
 
         // Remove any unclaimed mappings
@@ -2653,7 +2653,8 @@
     void writeKernelMappingLPr(SharedUserSetting sus) {
         if (mKernelMappingFilename == null || sus == null || sus.name == null) return;
 
-        writeKernelMappingLPr(sus.getSandboxName(), sus.userId, sus.getNotInstalledUserIds());
+        writeKernelMappingLPr(sus.getStorageSandboxName(),
+                sus.userId, sus.getNotInstalledUserIds());
     }
 
     void writeKernelMappingLPr(PackageSetting ps) {
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 32826e5..d67144e 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.os.storage.StorageManager;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
@@ -166,8 +167,8 @@
         return excludedUserIds == null ? EmptyArray.INT : excludedUserIds;
     }
 
-    public String getSandboxName() {
-        return "shared:" + name;
+    public String getStorageSandboxName() {
+        return StorageManager.SHARED_SANDBOX_PREFIX + name;
     }
 
     /** Updates all fields in this shared user setting from another. */
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index cef484f..01d02d6 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -41,6 +41,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.fingerprint.FingerprintManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkRequest;
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
@@ -271,6 +274,12 @@
             Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
         }
 
+        // Default NetworkRequest should cover all transport types.
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        final ConnectivityManager connectivityManager =
+                (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
+
         HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
         mHandler = new CompanionHandler(handlerThread.getLooper());
@@ -1875,4 +1884,19 @@
                     temp.getValue());
         }
     }
+
+    private static final class ConnectivityStatsCallback extends
+            ConnectivityManager.NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+                    StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+                    StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 10121c4..3e07ebe 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -37,6 +37,7 @@
 import android.service.notification.NotificationStats;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -671,6 +672,20 @@
         // Ensure state for the current user is applied, even if passed a non-current user.
         final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
         final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
+
+        // TODO(b/113914868): investigation log for disappearing home button
+        if (whichFlag == 1 && pkg.contains("systemui")) {
+            String disabledData = "{ ";
+            for (int i = 0; i < mDisableRecords.size(); i++) {
+                DisableRecord tok = mDisableRecords.get(i);
+                disabledData += "    ([" + i + "] " + tok + "), ";
+            }
+            disabledData += " }";
+            Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1
+                    + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => "
+                    + disabledData);
+        }
+
         if (net1 != mDisabled1 || net2 != mDisabled2) {
             mDisabled1 = net1;
             mDisabled2 = net2;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d30cd19..3cece11 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1749,6 +1749,7 @@
                 .setName(getSurfaceControl() + " - animation-bounds")
                 .setSize(getSurfaceWidth(), getSurfaceHeight());
         final SurfaceControl boundsLayer = builder.build();
+        t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight());
         t.show(boundsLayer);
         return boundsLayer;
     }
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index e358ad5..9633864 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -52,7 +52,7 @@
                     .setColorLayer(true)
                     .setParent(null) // TODO: Work-around for b/69259549
                     .build();
-
+            transaction.setWindowCrop(surface, w, h);
             transaction.setLayerStack(surface, dc.getDisplayId());
             transaction.setAlpha(surface, 1);
             transaction.setLayer(surface, layer);
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 65c8e96..cc14afc 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -126,9 +126,10 @@
         DimState(SurfaceControl dimLayer) {
             mDimLayer = dimLayer;
             mDimming = true;
-            mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
+            final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
+            mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
                 if (!mDimming) {
-                    mDimLayer.destroy();
+                    dimAnimatable.getPendingTransaction().destroy(mDimLayer);
                 }
             }, mHost.mService);
         }
@@ -309,6 +310,7 @@
             // TODO: Once we use geometry from hierarchy this falls away.
             t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
             t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
+            t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
             if (!mDimState.isVisible) {
                 mDimState.isVisible = true;
                 t.show(mDimState.mDimLayer);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 4eb021c..b49d304 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -187,6 +187,7 @@
                 }
                 t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
                 t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
+                t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
                 t.show(mSurface);
             } else if (mSurface != null) {
                 t.hide(mSurface);
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 3ea615a..66063c40 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -16,12 +16,12 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER;
 import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED;
 import static com.android.server.wm.SurfaceAnimatorProto.LEASH;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -305,6 +305,7 @@
                 .setName(surface + " - animation-leash")
                 .setSize(width, height);
         final SurfaceControl leash = builder.build();
+        t.setWindowCrop(surface, width, height);
         if (!hidden) {
             t.show(leash);
         }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 3493111..073601d 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -259,6 +259,7 @@
         final Rect stackBounds = getBounds();
         getPendingTransaction()
                 .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
+                .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
                 .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
                         mTmpRect.top - stackBounds.top);
         scheduleAnimation();
@@ -789,6 +790,7 @@
             return;
         }
         transaction.setSize(mSurfaceControl, width, height);
+        transaction.setWindowCrop(mSurfaceControl, width, height);
         mLastSurfaceSize.set(width, height);
     }
 
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
new file mode 100644
index 0000000..77b7347
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+import javax.crypto.SecretKey;
+
+/** Tests for {@link ContentDefinedChunker}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class ContentDefinedChunkerTest {
+    private static final int WINDOW_SIZE_BYTES = 31;
+    private static final int MIN_SIZE_BYTES = 40;
+    private static final int MAX_SIZE_BYTES = 300;
+    private static final String CHUNK_BOUNDARY = "<----------BOUNDARY----------->";
+    private static final byte[] CHUNK_BOUNDARY_BYTES = CHUNK_BOUNDARY.getBytes(UTF_8);
+    private static final String CHUNK_1 = "This is the first chunk";
+    private static final String CHUNK_2 = "And this is the second chunk";
+    private static final String CHUNK_3 = "And finally here is the third chunk";
+    private static final String SMALL_CHUNK = "12345678";
+
+    private FingerprintMixer mFingerprintMixer;
+    private RabinFingerprint64 mRabinFingerprint64;
+    private ContentDefinedChunker mChunker;
+
+    /** Set up a {@link ContentDefinedChunker} and dependencies for use in the tests. */
+    @Before
+    public void setUp() throws Exception {
+        SecretKey secretKey = generateAesKey();
+        byte[] salt = new byte[FingerprintMixer.SALT_LENGTH_BYTES];
+        Random random = new Random();
+        random.nextBytes(salt);
+        mFingerprintMixer = new FingerprintMixer(secretKey, salt);
+
+        mRabinFingerprint64 = new RabinFingerprint64();
+        long chunkBoundaryFingerprint = calculateFingerprint(CHUNK_BOUNDARY_BYTES);
+        mChunker =
+                new ContentDefinedChunker(
+                        MIN_SIZE_BYTES,
+                        MAX_SIZE_BYTES,
+                        mRabinFingerprint64,
+                        mFingerprintMixer,
+                        (fingerprint) -> fingerprint == chunkBoundaryFingerprint);
+    }
+
+    /**
+     * Creating a {@link ContentDefinedChunker} with a minimum chunk size that is smaller than the
+     * window size should throw an {@link IllegalArgumentException}.
+     */
+    @Test
+    public void create_withMinChunkSizeSmallerThanWindowSize_throwsIllegalArgumentException() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new ContentDefinedChunker(
+                                WINDOW_SIZE_BYTES - 1,
+                                MAX_SIZE_BYTES,
+                                mRabinFingerprint64,
+                                mFingerprintMixer,
+                                null));
+    }
+
+    /**
+     * Creating a {@link ContentDefinedChunker} with a maximum chunk size that is smaller than the
+     * minimum chunk size should throw an {@link IllegalArgumentException}.
+     */
+    @Test
+    public void create_withMaxChunkSizeSmallerThanMinChunkSize_throwsIllegalArgumentException() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->
+                        new ContentDefinedChunker(
+                                MIN_SIZE_BYTES,
+                                MIN_SIZE_BYTES - 1,
+                                mRabinFingerprint64,
+                                mFingerprintMixer,
+                                null));
+    }
+
+    /**
+     * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should split the
+     * input stream across chunk boundaries by default.
+     */
+    @Test
+    public void chunkify_withLargeChunks_splitsIntoChunksAcrossBoundaries() throws Exception {
+        byte[] input =
+                (CHUNK_1 + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8);
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
+        ArrayList<String> result = new ArrayList<>();
+
+        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
+
+        assertThat(result)
+                .containsExactly(CHUNK_1 + CHUNK_BOUNDARY, CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3)
+                .inOrder();
+    }
+
+    /** Chunks should be combined across boundaries until they reach the minimum chunk size. */
+    @Test
+    public void chunkify_withSmallChunks_combinesChunksUntilMinSize() throws Exception {
+        byte[] input =
+                (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY + CHUNK_3).getBytes(UTF_8);
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
+        ArrayList<String> result = new ArrayList<>();
+
+        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
+
+        assertThat(result)
+                .containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2 + CHUNK_BOUNDARY, CHUNK_3)
+                .inOrder();
+        assertThat(result.get(0).length()).isAtLeast(MIN_SIZE_BYTES);
+    }
+
+    /** Chunks can not be larger than the maximum chunk size. */
+    @Test
+    public void chunkify_doesNotProduceChunksLargerThanMaxSize() throws Exception {
+        byte[] largeInput = new byte[MAX_SIZE_BYTES * 10];
+        Arrays.fill(largeInput, "a".getBytes(UTF_8)[0]);
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(largeInput);
+        ArrayList<String> result = new ArrayList<>();
+
+        mChunker.chunkify(inputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
+
+        byte[] expectedChunkBytes = new byte[MAX_SIZE_BYTES];
+        Arrays.fill(expectedChunkBytes, "a".getBytes(UTF_8)[0]);
+        String expectedChunk = new String(expectedChunkBytes, UTF_8);
+        assertThat(result)
+                .containsExactly(
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk,
+                        expectedChunk)
+                .inOrder();
+    }
+
+    /**
+     * If the input stream signals zero availablility, {@link
+     * ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should still work.
+     */
+    @Test
+    public void chunkify_withInputStreamReturningZeroAvailability_returnsChunks() throws Exception {
+        byte[] input = (SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).getBytes(UTF_8);
+        ZeroAvailabilityInputStream zeroAvailabilityInputStream =
+                new ZeroAvailabilityInputStream(input);
+        ArrayList<String> result = new ArrayList<>();
+
+        mChunker.chunkify(
+                zeroAvailabilityInputStream, (chunk) -> result.add(new String(chunk, UTF_8)));
+
+        assertThat(result).containsExactly(SMALL_CHUNK + CHUNK_BOUNDARY + CHUNK_2).inOrder();
+    }
+
+    /**
+     * {@link ContentDefinedChunker#chunkify(InputStream, Chunker.ChunkConsumer)} should rethrow any
+     * exception thrown by its consumer.
+     */
+    @Test
+    public void chunkify_whenConsumerThrowsException_rethrowsException() throws Exception {
+        ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[] {1});
+
+        assertThrows(
+                GeneralSecurityException.class,
+                () ->
+                        mChunker.chunkify(
+                                inputStream,
+                                (chunk) -> {
+                                    throw new GeneralSecurityException();
+                                }));
+    }
+
+    private long calculateFingerprint(byte[] bytes) {
+        long fingerprint = 0;
+        for (byte inByte : bytes) {
+            fingerprint =
+                    mRabinFingerprint64.computeFingerprint64(
+                            /*inChar=*/ inByte, /*outChar=*/ (byte) 0, fingerprint);
+        }
+        return mFingerprintMixer.mix(fingerprint);
+    }
+
+    private static class ZeroAvailabilityInputStream extends ByteArrayInputStream {
+        ZeroAvailabilityInputStream(byte[] wrapped) {
+            super(wrapped);
+        }
+
+        @Override
+        public synchronized int available() {
+            return 0;
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
new file mode 100644
index 0000000..936b5dc
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.util.HashSet;
+import java.util.Random;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+/** Tests for {@link FingerprintMixer}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class FingerprintMixerTest {
+    private static final String KEY_ALGORITHM = "AES";
+    private static final int SEED = 42;
+    private static final int SALT_LENGTH_BYTES = 256 / 8;
+    private static final int KEY_SIZE_BITS = 256;
+
+    private Random mSeededRandom;
+    private FingerprintMixer mFingerprintMixer;
+
+    /** Set up a {@link FingerprintMixer} with deterministic key and salt generation. */
+    @Before
+    public void setUp() throws Exception {
+        // Seed so that the tests are deterministic.
+        mSeededRandom = new Random(SEED);
+        mFingerprintMixer = new FingerprintMixer(randomKey(), randomSalt());
+    }
+
+    /**
+     * Construcing a {@link FingerprintMixer} with a salt that is too small should throw an {@link
+     * IllegalArgumentException}.
+     */
+    @Test
+    public void create_withIncorrectSaltSize_throwsIllegalArgumentException() {
+        byte[] tooSmallSalt = new byte[SALT_LENGTH_BYTES - 1];
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> new FingerprintMixer(randomKey(), tooSmallSalt));
+    }
+
+    /**
+     * Constructing a {@link FingerprintMixer} with a secret key that can't be encoded should throw
+     * an {@link InvalidKeyException}.
+     */
+    @Test
+    public void create_withUnencodableSecretKey_throwsInvalidKeyException() {
+        byte[] keyBytes = new byte[KEY_SIZE_BITS / 8];
+        UnencodableSecretKeySpec keySpec =
+                new UnencodableSecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM);
+
+        assertThrows(InvalidKeyException.class, () -> new FingerprintMixer(keySpec, randomSalt()));
+    }
+
+    /**
+     * {@link FingerprintMixer#getAddend()} should not return the same addend for two different
+     * keys.
+     */
+    @Test
+    public void getAddend_withDifferentKey_returnsDifferentResult() throws Exception {
+        int iterations = 100_000;
+        HashSet<Long> returnedAddends = new HashSet<>();
+        byte[] salt = randomSalt();
+
+        for (int i = 0; i < iterations; i++) {
+            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt);
+            long addend = fingerprintMixer.getAddend();
+            returnedAddends.add(addend);
+        }
+
+        assertThat(returnedAddends).containsNoDuplicates();
+    }
+
+    /**
+     * {@link FingerprintMixer#getMultiplicand()} should not return the same multiplicand for two
+     * different keys.
+     */
+    @Test
+    public void getMultiplicand_withDifferentKey_returnsDifferentResult() throws Exception {
+        int iterations = 100_000;
+        HashSet<Long> returnedMultiplicands = new HashSet<>();
+        byte[] salt = randomSalt();
+
+        for (int i = 0; i < iterations; i++) {
+            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), salt);
+            long multiplicand = fingerprintMixer.getMultiplicand();
+            returnedMultiplicands.add(multiplicand);
+        }
+
+        assertThat(returnedMultiplicands).containsNoDuplicates();
+    }
+
+    /** The multiplicant returned by {@link FingerprintMixer} should always be odd. */
+    @Test
+    public void getMultiplicand_isOdd() throws Exception {
+        int iterations = 100_000;
+
+        for (int i = 0; i < iterations; i++) {
+            FingerprintMixer fingerprintMixer = new FingerprintMixer(randomKey(), randomSalt());
+
+            long multiplicand = fingerprintMixer.getMultiplicand();
+
+            assertThat(isOdd(multiplicand)).isTrue();
+        }
+    }
+
+    /** {@link FingerprintMixer#mix(long)} should have a random distribution. */
+    @Test
+    public void mix_randomlyDistributesBits() throws Exception {
+        int iterations = 100_000;
+        float tolerance = 0.1f;
+        int[] totals = new int[64];
+
+        for (int i = 0; i < iterations; i++) {
+            long n = mFingerprintMixer.mix(mSeededRandom.nextLong());
+            for (int j = 0; j < 64; j++) {
+                int bit = (int) (n >> j & 1);
+                totals[j] += bit;
+            }
+        }
+
+        for (int i = 0; i < 64; i++) {
+            float mean = ((float) totals[i]) / iterations;
+            float diff = Math.abs(mean - 0.5f);
+            assertThat(diff).isLessThan(tolerance);
+        }
+    }
+
+    /**
+     * {@link FingerprintMixer#mix(long)} should always produce a number that's different from the
+     * input.
+     */
+    @Test
+    public void mix_doesNotProduceSameNumberAsInput() {
+        int iterations = 100_000;
+
+        for (int i = 0; i < iterations; i++) {
+            assertThat(mFingerprintMixer.mix(i)).isNotEqualTo(i);
+        }
+    }
+
+    private byte[] randomSalt() {
+        byte[] salt = new byte[SALT_LENGTH_BYTES];
+        mSeededRandom.nextBytes(salt);
+        return salt;
+    }
+
+    /**
+     * Not a secure way of generating keys. We want to deterministically generate the same keys for
+     * each test run, though, to ensure the test is deterministic.
+     */
+    private SecretKey randomKey() {
+        byte[] keyBytes = new byte[KEY_SIZE_BITS / 8];
+        mSeededRandom.nextBytes(keyBytes);
+        return new SecretKeySpec(keyBytes, 0, keyBytes.length, KEY_ALGORITHM);
+    }
+
+    private static boolean isOdd(long n) {
+        return Math.abs(n % 2) == 1;
+    }
+
+    /**
+     * Subclass of {@link SecretKeySpec} that does not provide an encoded version. As per its
+     * contract in {@link Key}, that means {@code getEncoded()} always returns null.
+     */
+    private class UnencodableSecretKeySpec extends SecretKeySpec {
+        UnencodableSecretKeySpec(byte[] key, int offset, int len, String algorithm) {
+            super(key, offset, len, algorithm);
+        }
+
+        @Override
+        public byte[] getEncoded() {
+            return null;
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
new file mode 100644
index 0000000..5494374
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link Hkdf}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class HkdfTest {
+    /** HKDF Test Case 1 IKM from RFC 5869 */
+    private static final byte[] HKDF_CASE1_IKM = {
+        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+        0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+        0x0b, 0x0b
+    };
+
+    /** HKDF Test Case 1 salt from RFC 5869 */
+    private static final byte[] HKDF_CASE1_SALT = {
+        0x00, 0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08, 0x09,
+        0x0a, 0x0b, 0x0c
+    };
+
+    /** HKDF Test Case 1 info from RFC 5869 */
+    private static final byte[] HKDF_CASE1_INFO = {
+        (byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4,
+        (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9
+    };
+
+    /** First 32 bytes of HKDF Test Case 1 OKM (output) from RFC 5869 */
+    private static final byte[] HKDF_CASE1_OKM = {
+        (byte) 0x3c, (byte) 0xb2, (byte) 0x5f, (byte) 0x25, (byte) 0xfa,
+        (byte) 0xac, (byte) 0xd5, (byte) 0x7a, (byte) 0x90, (byte) 0x43,
+        (byte) 0x4f, (byte) 0x64, (byte) 0xd0, (byte) 0x36, (byte) 0x2f,
+        (byte) 0x2a, (byte) 0x2d, (byte) 0x2d, (byte) 0x0a, (byte) 0x90,
+        (byte) 0xcf, (byte) 0x1a, (byte) 0x5a, (byte) 0x4c, (byte) 0x5d,
+        (byte) 0xb0, (byte) 0x2d, (byte) 0x56, (byte) 0xec, (byte) 0xc4,
+        (byte) 0xc5, (byte) 0xbf
+    };
+
+    /** Test the example from RFC 5869. */
+    @Test
+    public void hkdf_derivesKeyMaterial() throws Exception {
+        byte[] result = Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, HKDF_CASE1_INFO);
+
+        assertThat(result).isEqualTo(HKDF_CASE1_OKM);
+    }
+
+    /** Providing a key that is null should throw a {@link java.lang.NullPointerException}. */
+    @Test
+    public void hkdf_withNullKey_throwsNullPointerException() throws Exception {
+        assertThrows(
+                NullPointerException.class,
+                () -> Hkdf.hkdf(null, HKDF_CASE1_SALT, HKDF_CASE1_INFO));
+    }
+
+    /** Providing a salt that is null should throw a {@link java.lang.NullPointerException}. */
+    @Test
+    public void hkdf_withNullSalt_throwsNullPointerException() throws Exception {
+        assertThrows(
+                NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, null, HKDF_CASE1_INFO));
+    }
+
+    /** Providing data that is null should throw a {@link java.lang.NullPointerException}. */
+    @Test
+    public void hkdf_withNullData_throwsNullPointerException() throws Exception {
+        assertThrows(
+                NullPointerException.class, () -> Hkdf.hkdf(HKDF_CASE1_IKM, HKDF_CASE1_SALT, null));
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
new file mode 100644
index 0000000..277dc37
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Random;
+
+/** Tests for {@link IsChunkBreakpoint}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class IsChunkBreakpointTest {
+    private static final int RANDOM_SEED = 42;
+    private static final double TOLERANCE = 0.01;
+    private static final int NUMBER_OF_TESTS = 10000;
+    private static final int BITS_PER_LONG = 64;
+
+    private Random mRandom;
+
+    /** Make sure that tests are deterministic. */
+    @Before
+    public void setUp() {
+        mRandom = new Random(RANDOM_SEED);
+    }
+
+    /**
+     * Providing a negative average number of trials should throw an {@link
+     * IllegalArgumentException}.
+     */
+    @Test
+    public void create_withNegativeAverageNumberOfTrials_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () -> new IsChunkBreakpoint(-1));
+    }
+
+    // Note: the following three tests are compute-intensive, so be cautious adding more.
+
+    /**
+     * If the provided average number of trials is zero, a breakpoint should be expected after one
+     * trial on average.
+     */
+    @Test
+    public void
+            isBreakpoint_withZeroAverageNumberOfTrials_isTrueOnAverageAfterOneTrial() {
+        assertExpectedTrials(new IsChunkBreakpoint(0), /*expectedTrials=*/ 1);
+    }
+
+    /**
+     * If the provided average number of trials is 512, a breakpoint should be expected after 512
+     * trials on average.
+     */
+    @Test
+    public void
+            isBreakpoint_with512AverageNumberOfTrials_isTrueOnAverageAfter512Trials() {
+        assertExpectedTrials(new IsChunkBreakpoint(512), /*expectedTrials=*/ 512);
+    }
+
+    /**
+     * If the provided average number of trials is 1024, a breakpoint should be expected after 1024
+     * trials on average.
+     */
+    @Test
+    public void
+            isBreakpoint_with1024AverageNumberOfTrials_isTrueOnAverageAfter1024Trials() {
+        assertExpectedTrials(new IsChunkBreakpoint(1024), /*expectedTrials=*/ 1024);
+    }
+
+    /** The number of leading zeros should be the logarithm of the average number of trials. */
+    @Test
+    public void getLeadingZeros_squaredIsAverageNumberOfTrials() {
+        for (int i = 0; i < BITS_PER_LONG; i++) {
+            long averageNumberOfTrials = (long) Math.pow(2, i);
+
+            int leadingZeros = new IsChunkBreakpoint(averageNumberOfTrials).getLeadingZeros();
+
+            assertThat(leadingZeros).isEqualTo(i);
+        }
+    }
+
+    private void assertExpectedTrials(IsChunkBreakpoint isChunkBreakpoint, long expectedTrials) {
+        long sum = 0;
+        for (int i = 0; i < NUMBER_OF_TESTS; i++) {
+            sum += numberOfTrialsTillBreakpoint(isChunkBreakpoint);
+        }
+        long averageTrials = sum / NUMBER_OF_TESTS;
+        assertThat((double) Math.abs(averageTrials - expectedTrials))
+                .isLessThan(TOLERANCE * expectedTrials);
+    }
+
+    private int numberOfTrialsTillBreakpoint(IsChunkBreakpoint isChunkBreakpoint) {
+        int trials = 0;
+
+        while (true) {
+            trials++;
+            if (isChunkBreakpoint.isBreakpoint(mRandom.nextLong())) {
+                return trials;
+            }
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
new file mode 100644
index 0000000..729580c
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking.cdc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link RabinFingerprint64}. */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class RabinFingerprint64Test {
+    private static final int WINDOW_SIZE = 31;
+    private static final ImmutableList<String> TEST_STRINGS =
+            ImmutableList.of(
+                    "ervHTtChYXO6eXivYqThlyyzqkbRaOR",
+                    "IxaVunH9ZC3qneWfhj1GkBH4ys9CYqz",
+                    "wZRVjlE1p976icCFPX9pibk4PEBvjSH",
+                    "pHIVaT8x8If9D6s9croksgNmJpmGYWI");
+
+    private final RabinFingerprint64 mRabinFingerprint64 = new RabinFingerprint64();
+
+    /**
+     * No matter where in the input buffer a string occurs, {@link
+     * RabinFingerprint64#computeFingerprint64(byte, byte, long)} should return the same
+     * fingerprint.
+     */
+    @Test
+    public void computeFingerprint64_forSameWindow_returnsSameFingerprint() {
+        long fingerprint1 =
+                computeFingerprintAtPosition(getBytes(TEST_STRINGS.get(0)), WINDOW_SIZE - 1);
+        long fingerprint2 =
+                computeFingerprintAtPosition(
+                        getBytes(TEST_STRINGS.get(1), TEST_STRINGS.get(0)), WINDOW_SIZE * 2 - 1);
+        long fingerprint3 =
+                computeFingerprintAtPosition(
+                        getBytes(TEST_STRINGS.get(2), TEST_STRINGS.get(3), TEST_STRINGS.get(0)),
+                        WINDOW_SIZE * 3 - 1);
+        String stub = "abc";
+        long fingerprint4 =
+                computeFingerprintAtPosition(
+                        getBytes(stub, TEST_STRINGS.get(0)), WINDOW_SIZE + stub.length() - 1);
+
+        // Assert that all fingerprints are exactly the same
+        assertThat(ImmutableSet.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
+                .hasSize(1);
+    }
+
+    /** The computed fingerprint should be different for different inputs. */
+    @Test
+    public void computeFingerprint64_withDifferentInput_returnsDifferentFingerprint() {
+        long fingerprint1 = computeFingerprintOf(TEST_STRINGS.get(0));
+        long fingerprint2 = computeFingerprintOf(TEST_STRINGS.get(1));
+        long fingerprint3 = computeFingerprintOf(TEST_STRINGS.get(2));
+        long fingerprint4 = computeFingerprintOf(TEST_STRINGS.get(3));
+
+        assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
+                .containsNoDuplicates();
+    }
+
+    /**
+     * An input with the same characters in a different order should return a different fingerprint.
+     */
+    @Test
+    public void computeFingerprint64_withSameInputInDifferentOrder_returnsDifferentFingerprint() {
+        long fingerprint1 = computeFingerprintOf("abcdefghijklmnopqrstuvwxyz12345");
+        long fingerprint2 = computeFingerprintOf("54321zyxwvutsrqponmlkjihgfedcba");
+        long fingerprint3 = computeFingerprintOf("4bcdefghijklmnopqrstuvwxyz123a5");
+        long fingerprint4 = computeFingerprintOf("bacdefghijklmnopqrstuvwxyz12345");
+
+        assertThat(ImmutableList.of(fingerprint1, fingerprint2, fingerprint3, fingerprint4))
+                .containsNoDuplicates();
+    }
+
+    /** UTF-8 bytes of all the given strings in order. */
+    private byte[] getBytes(String... strings) {
+        StringBuilder sb = new StringBuilder();
+        for (String s : strings) {
+            sb.append(s);
+        }
+        return sb.toString().getBytes(UTF_8);
+    }
+
+    /**
+     * The Rabin fingerprint of a window of bytes ending at {@code position} in the {@code bytes}
+     * array.
+     */
+    private long computeFingerprintAtPosition(byte[] bytes, int position) {
+        assertThat(position).isAtMost(bytes.length - 1);
+        long fingerprint = 0;
+        for (int i = 0; i <= position; i++) {
+            byte outChar;
+            if (i >= WINDOW_SIZE) {
+                outChar = bytes[i - WINDOW_SIZE];
+            } else {
+                outChar = (byte) 0;
+            }
+            fingerprint =
+                    mRabinFingerprint64.computeFingerprint64(
+                            /*inChar=*/ bytes[i], outChar, fingerprint);
+        }
+        return fingerprint;
+    }
+
+    private long computeFingerprintOf(String s) {
+        assertThat(s.length()).isEqualTo(WINDOW_SIZE);
+        return computeFingerprintAtPosition(s.getBytes(UTF_8), WINDOW_SIZE - 1);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index bd6ede2..a69f007 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -25,9 +25,12 @@
 import static android.app.backup.ForwardingBackupAgent.forward;
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createInitializedBackupManagerService;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBackupManagerServiceBasics;
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.setUpBinderCallerAndApplicationAsSystem;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .createInitializedBackupManagerService;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .setUpBackupManagerServiceBasics;
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils
+        .setUpBinderCallerAndApplicationAsSystem;
 import static com.android.server.backup.testing.PackageData.PM_PACKAGE;
 import static com.android.server.backup.testing.PackageData.fullBackupPackage;
 import static com.android.server.backup.testing.PackageData.keyValuePackage;
@@ -327,6 +330,22 @@
                 .isEqualTo("packageState".getBytes());
     }
 
+    /**
+     * Do not update backup token if the backup queue was empty
+     */
+    @Test
+    public void testRunTask_whenQueueEmptyOnFirstBackup_doesNotUpdateCurrentToken()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true);
+        mBackupManagerService.setCurrentToken(0L);
+        when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L);
+
+        runTask(task);
+
+        assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(0L);
+    }
+
     @Test
     public void testRunTask_whenOnePackageAndTransportUnavailable() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport.unavailable());
@@ -2297,6 +2316,24 @@
         expectThrows(IllegalArgumentException.class, () -> task.handleCancel(false));
     }
 
+    /**
+     * Do not update backup token if no data was moved.
+     */
+    @Test
+    public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotUpdateCurrentToken()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        mBackupManagerService.setCurrentToken(0L);
+        when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L);
+        // Set up agent with no data.
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(0L);
+    }
+
     private void runTask(KeyValueBackupTask task) {
         // Pretend we are not on the main-thread to prevent RemoteCall from complaining
         mShadowMainLooper.setCurrentThread(false);
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index ee42ce8..e6b328a 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -125,7 +125,7 @@
                 "/storage/emulated/0/foo.jpg",
                 PID_GREY, UID_GREY);
         assertTranslation(
-                "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
+                "/storage/emulated/0/Android/sandbox/shared-colors/foo.jpg",
                 "/storage/emulated/0/foo.jpg",
                 PID_RED, UID_COLORS);
     }
@@ -137,7 +137,7 @@
                 "/storage/0000-0000/foo/bar.jpg",
                 PID_GREY, UID_GREY);
         assertTranslation(
-                "/storage/0000-0000/Android/sandbox/shared:colors/foo/bar.jpg",
+                "/storage/0000-0000/Android/sandbox/shared-colors/foo/bar.jpg",
                 "/storage/0000-0000/foo/bar.jpg",
                 PID_RED, UID_COLORS);
     }
@@ -152,7 +152,7 @@
 
         // Accessing other package paths goes into sandbox
         assertTranslation(
-                "/storage/emulated/0/Android/sandbox/shared:colors/"
+                "/storage/emulated/0/Android/sandbox/shared-colors/"
                         + "Android/data/com.grey/foo.jpg",
                 "/storage/emulated/0/Android/data/com.grey/foo.jpg",
                 PID_RED, UID_COLORS);
@@ -201,7 +201,7 @@
         // Sandboxes can't see paths in other sandboxes
         try {
             mService.translateSystemToApp(
-                    "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
+                    "/storage/emulated/0/Android/sandbox/shared-colors/foo.jpg",
                     PID_GREY, UID_GREY);
             fail();
         } catch (SecurityException expected) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index b6a7cfb..991981f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -37,7 +37,7 @@
 
 /**
  * Build/Install/Run:
- *  atest FrameworksServicesTests:DimmerTests;
+ *  atest FrameworksServicesTests:DimmerTests
  */
 @Presubmit
 public class DimmerTests extends WindowTestsBase {
@@ -211,7 +211,7 @@
         mDimmer.updateDims(mTransaction, new Rect());
         verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
                 SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
-        verify(dimLayer).destroy();
+        verify(mHost.getPendingTransaction()).destroy(dimLayer);
     }
 
     @Test
diff --git a/telephony/java/android/telephony/CellConfigLte.java b/telephony/java/android/telephony/CellConfigLte.java
new file mode 100644
index 0000000..35769f0
--- /dev/null
+++ b/telephony/java/android/telephony/CellConfigLte.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * The container of LTE cell related configs.
+ * @hide
+ */
+public class CellConfigLte implements Parcelable {
+    private final boolean mIsEndcAvailable;
+
+    /** @hide */
+    public CellConfigLte() {
+        mIsEndcAvailable = false;
+    }
+
+    /** @hide */
+    public CellConfigLte(boolean isEndcAvailable) {
+        mIsEndcAvailable = isEndcAvailable;
+    }
+
+    /** @hide */
+    public CellConfigLte(CellConfigLte config) {
+        mIsEndcAvailable = config.mIsEndcAvailable;
+    }
+
+    /**
+     * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the LTE cell.
+     *
+     * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+     *
+     * @return {@code true} if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the LTE cell.
+     *
+     */
+    boolean isEndcAvailable() {
+        return mIsEndcAvailable;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsEndcAvailable);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellConfigLte)) return false;
+
+        CellConfigLte o = (CellConfigLte) other;
+        return mIsEndcAvailable == o.mIsEndcAvailable;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBoolean(mIsEndcAvailable);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder().append(this.getClass().getName())
+                .append(" :{")
+                .append(" isEndcAvailable = " + mIsEndcAvailable)
+                .append(" }")
+                .toString();
+    }
+
+    private CellConfigLte(Parcel in) {
+        mIsEndcAvailable = in.readBoolean();
+    }
+
+    public static final Creator<CellConfigLte> CREATOR = new Creator<CellConfigLte>() {
+        @Override
+        public CellConfigLte createFromParcel(Parcel in) {
+            return new CellConfigLte(in);
+        }
+
+        @Override
+        public CellConfigLte[] newArray(int size) {
+            return new CellConfigLte[0];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 389f643..7d5388b 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -19,7 +19,8 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
+
+import java.util.Objects;
 
 /**
  * A {@link CellInfo} representing an LTE cell that provides identity and measurement info.
@@ -31,6 +32,7 @@
 
     private CellIdentityLte mCellIdentityLte;
     private CellSignalStrengthLte mCellSignalStrengthLte;
+    private CellConfigLte mCellConfig;
 
     /** @hide */
     @UnsupportedAppUsage
@@ -38,6 +40,7 @@
         super();
         mCellIdentityLte = new CellIdentityLte();
         mCellSignalStrengthLte = new CellSignalStrengthLte();
+        mCellConfig = new CellConfigLte();
     }
 
     /** @hide */
@@ -45,6 +48,7 @@
         super(ci);
         this.mCellIdentityLte = ci.mCellIdentityLte.copy();
         this.mCellSignalStrengthLte = ci.mCellSignalStrengthLte.copy();
+        this.mCellConfig = new CellConfigLte(ci.mCellConfig);
     }
 
     @Override
@@ -71,26 +75,37 @@
         mCellSignalStrengthLte = css;
     }
 
+    /** @hide */
+    public void setCellConfig(CellConfigLte cellConfig) {
+        if (DBG) log("setCellConfig: " + cellConfig);
+        mCellConfig = cellConfig;
+    }
+
+    /** @hide */
+    public CellConfigLte getCellConfig() {
+        if (DBG) log("getCellConfig: " + mCellConfig);
+        return mCellConfig;
+    }
+
     /**
      * @return hash code
      */
     @Override
     public int hashCode() {
-        return super.hashCode() + mCellIdentityLte.hashCode() + mCellSignalStrengthLte.hashCode();
+        return Objects.hash(
+                super.hashCode(),
+                mCellIdentityLte.hashCode(),
+                mCellSignalStrengthLte.hashCode(),
+                mCellConfig.hashCode());
     }
 
     @Override
     public boolean equals(Object other) {
-        if (!super.equals(other)) {
-            return false;
-        }
-        try {
-            CellInfoLte o = (CellInfoLte) other;
-            return mCellIdentityLte.equals(o.mCellIdentityLte)
-                    && mCellSignalStrengthLte.equals(o.mCellSignalStrengthLte);
-        } catch (ClassCastException e) {
-            return false;
-        }
+        if (!(other instanceof CellInfoLte)) return false;
+        CellInfoLte o = (CellInfoLte) other;
+        return super.equals(o) && mCellIdentityLte.equals(o.mCellIdentityLte)
+                && mCellSignalStrengthLte.equals(o.mCellSignalStrengthLte)
+                && mCellConfig.equals(o.mCellConfig);
     }
 
     @Override
@@ -101,6 +116,7 @@
         sb.append(super.toString());
         sb.append(" ").append(mCellIdentityLte);
         sb.append(" ").append(mCellSignalStrengthLte);
+        sb.append(" ").append(mCellConfig);
         sb.append("}");
 
         return sb.toString();
@@ -119,6 +135,7 @@
         super.writeToParcel(dest, flags, TYPE_LTE);
         mCellIdentityLte.writeToParcel(dest, flags);
         mCellSignalStrengthLte.writeToParcel(dest, flags);
+        mCellConfig.writeToParcel(dest, flags);
     }
 
     /**
@@ -129,6 +146,7 @@
         super(in);
         mCellIdentityLte = CellIdentityLte.CREATOR.createFromParcel(in);
         mCellSignalStrengthLte = CellSignalStrengthLte.CREATOR.createFromParcel(in);
+        mCellConfig = CellConfigLte.CREATOR.createFromParcel(in);
         if (DBG) log("CellInfoLte(Parcel): " + toString());
     }
 
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index 97e3037..b6e6cba 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -17,17 +17,40 @@
      */
     public final int maxDataCalls;
 
-    DataSpecificRegistrationStates(int maxDataCalls) {
+    /**
+     * Indicates if the use of dual connectivity with NR is restricted.
+     * Reference: 3GPP TS 24.301 v15.03 section 9.3.3.12A.
+     */
+    public final boolean isDcNrRestricted;
+
+    /**
+     * Indicates if NR is supported by the selected PLMN.
+     *
+     * {@code true} if the bit N is in the PLMN-InfoList-r15 is true and the selected PLMN is
+     * present in plmn-IdentityList at position N.
+     * Reference: 3GPP TS 36.331 v15.2.2 section 6.3.1 PLMN-InfoList-r15.
+     *            3GPP TS 36.331 v15.2.2 section 6.2.2 SystemInformationBlockType1 message.
+     */
+    public final boolean isNrAvailable;
+
+    DataSpecificRegistrationStates(
+            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable) {
         this.maxDataCalls = maxDataCalls;
+        this.isDcNrRestricted = isDcNrRestricted;
+        this.isNrAvailable = isNrAvailable;
     }
 
     private DataSpecificRegistrationStates(Parcel source) {
         maxDataCalls = source.readInt();
+        isDcNrRestricted = source.readBoolean();
+        isNrAvailable = source.readBoolean();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(maxDataCalls);
+        dest.writeBoolean(isDcNrRestricted);
+        dest.writeBoolean(isNrAvailable);
     }
 
     @Override
@@ -37,24 +60,30 @@
 
     @Override
     public String toString() {
-        return "DataSpecificRegistrationStates {" + " mMaxDataCalls=" + maxDataCalls + "}";
+        return new StringBuilder().append(this.getClass().getName())
+                .append(" :{")
+                .append(" maxDataCalls = " + maxDataCalls)
+                .append(" isDcNrRestricted = " + isDcNrRestricted)
+                .append(" isNrAvailable = " + isNrAvailable)
+                .append(" }")
+                .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxDataCalls);
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable);
     }
 
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
 
-        if (o == null || !(o instanceof DataSpecificRegistrationStates)) {
-            return false;
-        }
+        if (!(o instanceof DataSpecificRegistrationStates)) return false;
 
         DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
-        return this.maxDataCalls == other.maxDataCalls;
+        return this.maxDataCalls == other.maxDataCalls
+                && this.isDcNrRestricted == other.isDcNrRestricted
+                && this.isNrAvailable == other.isNrAvailable;
     }
 
     public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index 68e512e..75e8eda 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -161,11 +161,9 @@
      * @hide
      */
     public NetworkRegistrationState(int domain, int transportType, int regState,
-                                    int accessNetworkTechnology, int rejectCause,
-                                    boolean emergencyOnly, int[] availableServices,
-                                    @Nullable CellIdentity cellIdentity, boolean cssSupported,
-                                    int roamingIndicator, int systemIsInPrl,
-                                    int defaultRoamingIndicator) {
+            int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, boolean cssSupported,
+            int roamingIndicator, int systemIsInPrl, int defaultRoamingIndicator) {
         this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
                 availableServices, cellIdentity);
 
@@ -178,13 +176,14 @@
      * @hide
      */
     public NetworkRegistrationState(int domain, int transportType, int regState,
-                                    int accessNetworkTechnology, int rejectCause,
-                                    boolean emergencyOnly, int[] availableServices,
-                                    @Nullable CellIdentity cellIdentity, int maxDataCalls) {
+            int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
+            int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
+            boolean isDcNrRestricted, boolean isNrAvailable) {
         this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
                 availableServices, cellIdentity);
 
-        mDataSpecificStates = new DataSpecificRegistrationStates(maxDataCalls);
+        mDataSpecificStates = new DataSpecificRegistrationStates(
+                maxDataCalls, isDcNrRestricted, isNrAvailable);
     }
 
     protected NetworkRegistrationState(Parcel source) {
@@ -345,7 +344,7 @@
     public boolean equals(Object o) {
         if (this == o) return true;
 
-        if (o == null || !(o instanceof NetworkRegistrationState)) {
+        if (!(o instanceof NetworkRegistrationState)) {
             return false;
         }
 
@@ -357,11 +356,10 @@
                 && mAccessNetworkTechnology == other.mAccessNetworkTechnology
                 && mRejectCause == other.mRejectCause
                 && mEmergencyOnly == other.mEmergencyOnly
-                && (mAvailableServices == other.mAvailableServices
-                    || Arrays.equals(mAvailableServices, other.mAvailableServices))
-                && equals(mCellIdentity, other.mCellIdentity)
-                && equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
-                && equals(mDataSpecificStates, other.mDataSpecificStates);
+                && Arrays.equals(mAvailableServices, other.mAvailableServices)
+                && Objects.equals(mCellIdentity, other.mCellIdentity)
+                && Objects.equals(mVoiceSpecificStates, other.mVoiceSpecificStates)
+                && Objects.equals(mDataSpecificStates, other.mDataSpecificStates);
     }
 
     @Override
@@ -391,14 +389,4 @@
             return new NetworkRegistrationState[size];
         }
     };
-
-    private static boolean equals(Object o1, Object o2) {
-        if (o1 == o2) {
-            return true;
-        } else if (o1 == null) {
-            return false;
-        } else {
-            return o1.equals(o2);
-        }
-    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a188ef6..bd1a0fb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9361,4 +9361,55 @@
         }
         return false;
     }
+
+    /**
+     * Set preferred opportunistic data subscription id.
+     *
+     * <p>Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param subId which opportunistic subscription
+     * {@link SubscriptionManager#getOpportunisticSubscriptions} is preferred for cellular data.
+     * Pass {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} to unset the preference
+     * @return true if request is accepted, else false.
+     *
+     */
+    public boolean setPreferredOpportunisticDataSubscription(int subId) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        try {
+            IAns iAlternativeNetworkService = getIAns();
+            if (iAlternativeNetworkService != null) {
+                return iAlternativeNetworkService.setPreferredData(subId, pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setPreferredData RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Get preferred opportunistic data subscription Id
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
+     * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+     * @return subId preferred opportunistic subscription id or
+     * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
+     * subscription id
+     *
+     */
+    public int getPreferredOpportunisticDataSubscription() {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            IAns iAlternativeNetworkService = getIAns();
+            if (iAlternativeNetworkService != null) {
+                subId = iAlternativeNetworkService.getPreferredData(pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getPreferredData RemoteException", ex);
+        }
+        return subId;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IAns.aidl b/telephony/java/com/android/internal/telephony/IAns.aidl
index 6eb8d66..e9a4649 100755
--- a/telephony/java/com/android/internal/telephony/IAns.aidl
+++ b/telephony/java/com/android/internal/telephony/IAns.aidl
@@ -49,4 +49,33 @@
     * @param callingPackage caller's package name
     */
     boolean isEnabled(String callingPackage);
+
+    /**
+     * Set preferred opportunistic data subscription id.
+     *
+     * <p>Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param subId which opportunistic subscription
+     * {@link SubscriptionManager#getOpportunisticSubscriptions} is preferred for cellular data.
+     * Pass {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} to unset the preference
+     * @param callingPackage caller's package name
+     * @return true if request is accepted, else false.
+     *
+     */
+    boolean setPreferredData(int subId, String callingPackage);
+
+    /**
+     * Get preferred opportunistic data subscription Id
+     *
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
+     * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+     * @return subId preferred opportunistic subscription id or
+     * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
+     * subscription id
+     *
+     */
+    int getPreferredData(String callingPackage);
 }
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index fc9b4c6..f91d74a 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -31,6 +31,7 @@
 
   public class MockContext extends android.content.Context {
     ctor public MockContext();
+    method public boolean bindIsolatedService(android.content.Intent, android.content.ServiceConnection, int, java.lang.String);
     method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
     method public int checkCallingOrSelfPermission(java.lang.String);
     method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index fa5b896..66be6d9 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -576,6 +576,13 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public boolean bindIsolatedService(Intent service,
+            ServiceConnection conn, int flags,
+            String instanceName) {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @Override
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java
index 6e4c41e..73db451 100644
--- a/test-runner/src/android/test/IsolatedContext.java
+++ b/test-runner/src/android/test/IsolatedContext.java
@@ -75,6 +75,12 @@
     }
 
     @Override
+    public boolean bindIsolatedService(Intent service, ServiceConnection conn, int flags,
+            String instanceName) {
+        return false;
+    }
+
+    @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
         return null;
     }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8fc9b97..b34ac26 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -58,7 +58,7 @@
     /**
      * Current Version of the Backup Serializer.
     */
-    private static final int BACKUP_VERSION = 2;
+    private static final int BACKUP_VERSION = 3;
     /** {@hide} */
     public static final String ssidVarName = "ssid";
     /** {@hide} */
@@ -2420,6 +2420,7 @@
         out.writeInt(apChannel);
         BackupUtils.writeString(out, preSharedKey);
         out.writeInt(getAuthType());
+        out.writeBoolean(hiddenSSID);
         return baos.toByteArray();
     }
 
@@ -2442,6 +2443,9 @@
         config.apChannel = in.readInt();
         config.preSharedKey = BackupUtils.readString(in);
         config.allowedKeyManagement.set(in.readInt());
+        if (version >= 3) {
+            config.hiddenSSID = in.readBoolean();
+        }
         return config;
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5f3e1b2..bf6feac 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
@@ -30,6 +31,9 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiConfiguration}.
  */
@@ -242,4 +246,30 @@
         config.setRandomizedMacAddress(null);
         assertEquals(defaultMac, config.getRandomizedMacAddress());
     }
+
+    /**
+     * Verifies that the serialization/de-serialization for softap config works.
+     */
+    @Test
+    public void testSoftApConfigBackupAndRestore() throws Exception {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "TestAP";
+        config.apBand = WifiConfiguration.AP_BAND_5GHZ;
+        config.apChannel = 40;
+        config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+        config.preSharedKey = "TestPsk";
+        config.hiddenSSID = true;
+
+        byte[] data = config.getBytesForBackup();
+        ByteArrayInputStream bais = new ByteArrayInputStream(data);
+        DataInputStream in = new DataInputStream(bais);
+        WifiConfiguration restoredConfig = WifiConfiguration.getWifiConfigFromBackup(in);
+
+        assertEquals(config.SSID, restoredConfig.SSID);
+        assertEquals(config.preSharedKey, restoredConfig.preSharedKey);
+        assertEquals(config.getAuthType(), restoredConfig.getAuthType());
+        assertEquals(config.apBand, restoredConfig.apBand);
+        assertEquals(config.apChannel, restoredConfig.apChannel);
+        assertEquals(config.hiddenSSID, restoredConfig.hiddenSSID);
+    }
 }