Merge "Add behavioral parameters to MediaParser"
diff --git a/Android.bp b/Android.bp
index ce47097..b6483a0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1088,13 +1088,6 @@
 }
 
 filegroup {
-    name: "framework-annotation-nonnull-srcs",
-    srcs: [
-        "core/java/android/annotation/NonNull.java",
-    ],
-}
-
-filegroup {
     name: "framework-media-annotation-srcs",
     srcs: [
         ":framework-annotations",
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index aae33d7..18b1108 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -858,12 +858,9 @@
         writeBlobsInfoAsync();
 
         // Cleanup any stale sessions.
-        final ArrayList<Integer> indicesToRemove = new ArrayList<>();
         for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
             final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
-            indicesToRemove.clear();
-            for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
-                final BlobStoreSession blobStoreSession = userSessions.valueAt(j);
+            userSessions.removeIf((sessionId, blobStoreSession) -> {
                 boolean shouldRemove = false;
 
                 // Cleanup sessions which haven't been modified in a while.
@@ -880,13 +877,10 @@
                 if (shouldRemove) {
                     blobStoreSession.getSessionFile().delete();
                     mActiveBlobIds.remove(blobStoreSession.getSessionId());
-                    indicesToRemove.add(j);
                     deletedBlobIds.add(blobStoreSession.getSessionId());
                 }
-            }
-            for (int j = 0; j < indicesToRemove.size(); ++j) {
-                userSessions.removeAt(indicesToRemove.get(j));
-            }
+                return shouldRemove;
+            });
         }
         if (LOGV) {
             Slog.v(TAG, "Completed idle maintenance; deleted "
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index b96161a..4c98b5f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -77,11 +77,11 @@
 
     /**
      * @hide
-     * @deprecated use {@link #getReasonCodeDescription(int)}
      */
-    @Deprecated
-    public static String getReasonName(int reason) {
-        switch (reason) {
+    // TODO(142420609): make it @SystemApi for mainline
+    @NonNull
+    public static String getReasonCodeDescription(int reasonCode) {
+        switch (reasonCode) {
             case REASON_CANCELED: return "canceled";
             case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
             case REASON_PREEMPT: return "preempt";
@@ -89,7 +89,7 @@
             case REASON_DEVICE_IDLE: return "device_idle";
             case REASON_DEVICE_THERMAL: return "thermal";
             case REASON_RESTRAINED: return "restrained";
-            default: return "unknown:" + reason;
+            default: return "unknown:" + reasonCode;
         }
     }
 
@@ -100,13 +100,6 @@
         return JOB_STOP_REASON_CODES;
     }
 
-    /** @hide */
-    // @SystemApi TODO make it a system api for mainline
-    @NonNull
-    public static String getReasonCodeDescription(int reasonCode) {
-        return getReasonName(reasonCode);
-    }
-
     @UnsupportedAppUsage
     private final int jobId;
     private final PersistableBundle extras;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
index e28e5bd..d050347 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
@@ -359,7 +359,8 @@
                             }
                             pw.print(pe.stopReasons.valueAt(k));
                             pw.print("x ");
-                            pw.print(JobParameters.getReasonName(pe.stopReasons.keyAt(k)));
+                            pw.print(JobParameters
+                                    .getReasonCodeDescription(pe.stopReasons.keyAt(k)));
                         }
                         pw.println();
                     }
@@ -606,8 +607,9 @@
                 if (reason != null) {
                     pw.print(mEventReasons[index]);
                 } else {
-                    pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK)
-                            >> EVENT_STOP_REASON_SHIFT));
+                    pw.print(JobParameters.getReasonCodeDescription(
+                            (mEventCmds[index] & EVENT_STOP_REASON_MASK)
+                                    >> EVENT_STOP_REASON_SHIFT));
                 }
             }
             pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ff7944d..c1e529f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1963,7 +1963,7 @@
                 if (restriction != null) {
                     final int reason = restriction.getReason();
                     serviceContext.cancelExecutingJobLocked(reason,
-                            "restricted due to " + JobParameters.getReasonName(reason));
+                            "restricted due to " + JobParameters.getReasonCodeDescription(reason));
                 }
             }
         }
@@ -3110,7 +3110,7 @@
                             final JobRestriction restriction = mJobRestrictions.get(i);
                             if (restriction.isJobRestricted(job)) {
                                 final int reason = restriction.getReason();
-                                pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]");
+                                pw.print(" " + JobParameters.getReasonCodeDescription(reason));
                             }
                         }
                     } else {
diff --git a/api/current.txt b/api/current.txt
index 849f1e4..476d0ae 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32,6 +32,7 @@
     field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
     field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
     field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
+    field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS";
     field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
     field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
     field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
@@ -6969,7 +6970,6 @@
     method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int);
     method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     method public boolean requestBugreport(@NonNull android.content.ComponentName);
-    method public void requestSetLocationProviderAllowed(@NonNull android.content.ComponentName, @NonNull String, boolean);
     method @Deprecated public boolean resetPassword(String, int);
     method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int);
     method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long);
@@ -24788,7 +24788,7 @@
   }
 
   public abstract class DrmInitData {
-    method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
+    method @Deprecated public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
     method @NonNull public android.media.DrmInitData.SchemeInitData getSchemeInitDataAt(int);
     method public int getSchemeInitDataCount();
   }
@@ -25307,6 +25307,10 @@
     method public void recycle();
   }
 
+  public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException {
+    ctor public MediaCodec.IncompatibleWithBlockModelException();
+  }
+
   public static final class MediaCodec.LinearBlock {
     method protected void finalize();
     method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
@@ -25334,23 +25338,24 @@
   }
 
   public static final class MediaCodec.OutputFrame {
-    method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>);
     method public int getFlags();
     method @NonNull public android.media.MediaFormat getFormat();
     method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock();
     method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
     method public long getPresentationTimeUs();
+    method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>);
   }
 
   public final class MediaCodec.QueueRequest {
     method public void queue();
     method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
-    method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
     method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
-    method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock);
     method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
-    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int);
+    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
     method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
+    method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
     method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
   }
 
@@ -43421,6 +43426,7 @@
     method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
     method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable();
     method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested();
+    method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control);
     field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
     field @NonNull public static final String TAG = "ControlsProviderService";
   }
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 7b66f73..6863221 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -30,7 +30,6 @@
   }
 
   public class TetheringConstants {
-    ctor public TetheringConstants();
     field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
     field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
index 6e59596..56f7a02 100644
--- a/api/module-lib-lint-baseline.txt
+++ b/api/module-lib-lint-baseline.txt
@@ -27,7 +27,3 @@
     Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
 PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
     Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
-
-
-StaticUtils: android.net.TetheringConstants:
-    Fully-static utility classes must not have constructor
diff --git a/api/test-current.txt b/api/test-current.txt
index 3bf0acd..9e37a3c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2538,6 +2538,11 @@
     method public void log(android.os.StrictMode.ViolationInfo);
   }
 
+  public static final class StrictMode.VmPolicy.Builder {
+    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
+    method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
+  }
+
   public class SystemConfigManager {
     method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
     method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6b5bfda..8df26cb 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,9 @@
 
 package android.app;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.StrictMode.vmIncorrectContextUseEnabled;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -64,6 +67,7 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -247,6 +251,7 @@
 
     private boolean mIsSystemOrSystemUiContext;
     private boolean mIsUiContext;
+    private boolean mIsAssociatedWithDisplay;
 
     @GuardedBy("mSync")
     private File mDatabasesDir;
@@ -1891,9 +1896,20 @@
 
     @Override
     public Object getSystemService(String name) {
-        if (isUiComponent(name) && !isUiContext()) {
-            Log.w(TAG, name + " should be accessed from Activity or other visual Context");
+        // Check incorrect Context usage.
+        if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
+            final String errorMessage = "Tried to access visual service " + name
+                    + " from a non-visual Context.";
+            final String message = "Visual services, such as WindowManager, WallpaperService or "
+                    + "LayoutInflater should be accessed from Activity or other visual Context. "
+                    + "Use an Activity or a Context created with "
+                    + "Context#createWindowContext(int, Bundle), which are adjusted to the "
+                    + "configuration and visual bounds of an area on screen.";
+            final Exception exception = new IllegalAccessException(errorMessage);
+            StrictMode.onIncorrectContextUsed(message, exception);
+            Log.e(TAG, errorMessage + message, exception);
         }
+
         return SystemServiceRegistry.getSystemService(this, name);
     }
 
@@ -1902,8 +1918,17 @@
         return SystemServiceRegistry.getSystemServiceName(serviceClass);
     }
 
-    boolean isUiContext() {
-        return mIsSystemOrSystemUiContext || mIsUiContext;
+    private boolean isUiContext() {
+        return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI();
+    }
+
+    /**
+     * Temporary workaround to permit incorrect usages of Context by SystemUI.
+     * TODO(b/149790106): Fix usages and remove.
+     */
+    private boolean isSystemOrSystemUI() {
+        return ActivityThread.isSystem() || checkPermission("android.permission.STATUS_BAR_SERVICE",
+                Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED;
     }
 
     private static boolean isUiComponent(String name) {
@@ -1925,7 +1950,7 @@
             final int appId = UserHandle.getAppId(uid);
             if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                 Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
-                return PackageManager.PERMISSION_GRANTED;
+                return PERMISSION_GRANTED;
             }
             Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                     + permission);
@@ -1989,7 +2014,7 @@
     private void enforce(
             String permission, int resultOfCheck,
             boolean selfToo, int uid, String message) {
-        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+        if (resultOfCheck != PERMISSION_GRANTED) {
             throw new SecurityException(
                     (message != null ? (message + ": ") : "") +
                     (selfToo
@@ -2116,15 +2141,15 @@
         if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
             if (readPermission == null
                     || checkPermission(readPermission, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
-                return PackageManager.PERMISSION_GRANTED;
+                    == PERMISSION_GRANTED) {
+                return PERMISSION_GRANTED;
             }
         }
         if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
             if (writePermission == null
                     || checkPermission(writePermission, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
-                return PackageManager.PERMISSION_GRANTED;
+                    == PERMISSION_GRANTED) {
+                return PERMISSION_GRANTED;
             }
         }
         return uri != null ? checkUriPermission(uri, pid, uid, modeFlags)
@@ -2157,7 +2182,7 @@
     private void enforceForUri(
             int modeFlags, int resultOfCheck, boolean selfToo,
             int uid, Uri uri, String message) {
-        if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+        if (resultOfCheck != PERMISSION_GRANTED) {
             throw new SecurityException(
                     (message != null ? (message + ": ") : "") +
                     (selfToo
@@ -2373,6 +2398,7 @@
                 null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
                 mResources.getLoaders()));
         context.mDisplay = display;
+        context.mIsAssociatedWithDisplay = true;
         return context;
     }
 
@@ -2390,6 +2416,7 @@
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
                 mSplitName, token, mUser, mFlags, mClassLoader, null);
         context.mIsUiContext = true;
+        context.mIsAssociatedWithDisplay = true;
         return context;
     }
 
@@ -2440,6 +2467,19 @@
 
     @Override
     public Display getDisplay() {
+        if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSystemOrSystemUI()) {
+            throw new UnsupportedOperationException("Tried to obtain display from a Context not "
+                    + "associated with  one. Only visual Contexts (such as Activity or one created "
+                    + "with Context#createWindowContext) or ones created with "
+                    + "Context#createDisplayContext are associated with displays. Other types of "
+                    + "Contexts are typically related to background entities and may return an "
+                    + "arbitrary display.");
+        }
+        return getDisplayNoVerify();
+    }
+
+    @Override
+    public Display getDisplayNoVerify() {
         if (mDisplay == null) {
             return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
                     mResources);
@@ -2450,13 +2490,14 @@
 
     @Override
     public int getDisplayId() {
-        final Display display = getDisplay();
+        final Display display = getDisplayNoVerify();
         return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
     }
 
     @Override
     public void updateDisplay(int displayId) {
         mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
+        mIsAssociatedWithDisplay = true;
     }
 
     @Override
@@ -2630,6 +2671,7 @@
         ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
                 activityInfo.splitName, activityToken, null, 0, classLoader, null);
         context.mIsUiContext = true;
+        context.mIsAssociatedWithDisplay = true;
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java
index 761b225..5ebcc46 100644
--- a/core/java/android/app/TaskEmbedder.java
+++ b/core/java/android/app/TaskEmbedder.java
@@ -597,7 +597,7 @@
         if (mTmpDisplayMetrics == null) {
             mTmpDisplayMetrics = new DisplayMetrics();
         }
-        mContext.getDisplay().getMetrics(mTmpDisplayMetrics);
+        mContext.getDisplayNoVerify().getRealMetrics(mTmpDisplayMetrics);
         return mTmpDisplayMetrics.densityDpi;
     }
 
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5f74d2e..d9405e1 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2097,7 +2097,7 @@
 
         public ColorManagementProxy(@NonNull Context context) {
             // Get a list of supported wide gamut color spaces.
-            Display display = context.getDisplay();
+            Display display = context.getDisplayNoVerify();
             if (display != null) {
                 mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut()));
             }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 546fef9..55be082 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8989,49 +8989,6 @@
     }
 
     /**
-     * Called by device owners to request a location provider to change its allowed state. For a
-     * provider to be enabled requires both that the master location setting is enabled, and that
-     * the provider itself is allowed. Most location providers are always allowed. Some location
-     * providers may have user consents or terms and conditions that must be accepted, or some other
-     * type of blocker before they are allowed however. Every location provider is responsible for
-     * its own allowed state.
-     *
-     * <p>This method requests that a location provider change its allowed state. For providers that
-     * are always allowed and have no state to change, this will have no effect. If the provider
-     * does require some consent, terms and conditions, or other blocking state, using this API
-     * implies that the device owner is agreeing/disagreeing to any consents, terms and conditions,
-     * etc, and the provider should make a best effort to adjust it's allowed state accordingly.
-     *
-     * <p>Location providers are generally only responsible for the current user, and callers must
-     * assume that this method will only affect provider state for the current user. Callers are
-     * responsible for tracking current user changes and re-updating provider state as necessary.
-     *
-     * <p>While providers are expected to make a best effort to honor this request, it is not a
-     * given that all providers will support such a request. If a provider does change its state as
-     * a result of this request, that may happen asynchronously after some delay. Test location
-     * providers set through {@link android.location.LocationManager#addTestProvider} will respond
-     * to this request to aide in testing.
-     *
-     * @param admin          Which {@link DeviceAdminReceiver} this request is associated with
-     * @param provider       A location provider as listed by
-     *                       {@link android.location.LocationManager#getAllProviders()}
-     * @param providerAllowed Whether the location provider is being requested to enable or disable
-     *                       itself
-     * @throws SecurityException if {@code admin} is not a device owner.
-     */
-    public void requestSetLocationProviderAllowed(@NonNull ComponentName admin,
-            @NonNull String provider, boolean providerAllowed) {
-        throwIfParentInstance("requestSetLocationProviderAllowed");
-        if (mService != null) {
-            try {
-                mService.requestSetLocationProviderAllowed(admin, provider, providerAllowed);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
      * Called by profile or device owners to update {@link android.provider.Settings.Secure}
      * settings. Validation that the value of the setting is in the correct form for the setting
      * type should be performed by the caller.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d161e06..da48663 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -272,7 +272,6 @@
     boolean hasLockdownAdminConfiguredNetworks(in ComponentName who);
 
     void setLocationEnabled(in ComponentName who, boolean locationEnabled);
-    void requestSetLocationProviderAllowed(in ComponentName who, in String provider, boolean providerAllowed);
 
     boolean setTime(in ComponentName who, long millis);
     boolean setTimeZone(in ComponentName who, String timeZone);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e1942da..bd3298c7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -580,6 +580,15 @@
         }
 
         @Override
+        public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+                RemoteCallback callback) {
+            final Bundle result = new Bundle();
+            result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+                    canonicalize(callingPkg, featureId, uri));
+            callback.sendResult(result);
+        }
+
+        @Override
         public Uri uncanonicalize(String callingPkg, String featureId,  Uri uri) {
             uri = validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 0f1442d..7bc5901 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -359,6 +359,16 @@
                     return true;
                 }
 
+                case CANONICALIZE_ASYNC_TRANSACTION: {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    String callingPkg = data.readString();
+                    String featureId = data.readString();
+                    Uri uri = Uri.CREATOR.createFromParcel(data);
+                    RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+                    canonicalizeAsync(callingPkg, featureId, uri, callback);
+                    return true;
+                }
+
                 case UNCANONICALIZE_TRANSACTION:
                 {
                     data.enforceInterface(IContentProvider.descriptor);
@@ -823,6 +833,25 @@
     }
 
     @Override
+    /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+            Uri uri, RemoteCallback callback) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeInterfaceToken(IContentProvider.descriptor);
+
+            data.writeString(callingPkg);
+            data.writeString(featureId);
+            uri.writeToParcel(data, 0);
+            callback.writeToParcel(data, 0);
+
+            mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null,
+                    Binder.FLAG_ONEWAY);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    @Override
     public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..b748cfa 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -712,14 +712,17 @@
      * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
      * @hide
      */
-    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
+    public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
             CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
 
+    // Timeout given a ContentProvider that has already been started and connected to.
+    private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;
+
     // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
     // long ActivityManagerService is giving a content provider to get published if a new process
     // needs to be started for that.
-    private static final int GET_TYPE_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
+    private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;
 
     public ContentResolver(@Nullable Context context) {
         this(context, null);
@@ -833,10 +836,10 @@
         IContentProvider provider = acquireExistingProvider(url);
         if (provider != null) {
             try {
-                final GetTypeResultListener resultListener = new GetTypeResultListener();
+                final StringResultListener resultListener = new StringResultListener();
                 provider.getTypeAsync(url, new RemoteCallback(resultListener));
-                resultListener.waitForResult();
-                return resultListener.type;
+                resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+                return resultListener.result;
             } catch (RemoteException e) {
                 // Arbitrary and not worth documenting, as Activity
                 // Manager will kill this process shortly anyway.
@@ -854,13 +857,13 @@
         }
 
         try {
-            GetTypeResultListener resultListener = new GetTypeResultListener();
+            final StringResultListener resultListener = new StringResultListener();
             ActivityManager.getService().getProviderMimeTypeAsync(
                     ContentProvider.getUriWithoutUserId(url),
                     resolveUserId(url),
                     new RemoteCallback(resultListener));
-            resultListener.waitForResult();
-            return resultListener.type;
+            resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            return resultListener.result;
         } catch (RemoteException e) {
             // We just failed to send a oneway request to the System Server. Nothing to do.
             return null;
@@ -870,27 +873,29 @@
         }
     }
 
-    private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
+    private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener {
         @GuardedBy("this")
         public boolean done;
 
         @GuardedBy("this")
-        public String type;
+        public T result;
 
         @Override
         public void onResult(Bundle result) {
             synchronized (this) {
-                type = result.getString(REMOTE_CALLBACK_RESULT);
+                this.result = getResultFromBundle(result);
                 done = true;
                 notifyAll();
             }
         }
 
-        public void waitForResult() {
+        protected abstract T getResultFromBundle(Bundle result);
+
+        public void waitForResult(long timeout) {
             synchronized (this) {
                 if (!done) {
                     try {
-                        wait(GET_TYPE_TIMEOUT_MILLIS);
+                        wait(timeout);
                     } catch (InterruptedException e) {
                         // Ignore
                     }
@@ -899,6 +904,20 @@
         }
     }
 
+    private static class StringResultListener extends ResultListener<String> {
+        @Override
+        protected String getResultFromBundle(Bundle result) {
+            return result.getString(REMOTE_CALLBACK_RESULT);
+        }
+    }
+
+    private static class UriResultListener extends ResultListener<Uri> {
+        @Override
+        protected Uri getResultFromBundle(Bundle result) {
+            return result.getParcelable(REMOTE_CALLBACK_RESULT);
+        }
+    }
+
     /**
      * Query for the possible MIME types for the representations the given
      * content URL can be returned when opened as as stream with
@@ -1192,7 +1211,11 @@
         }
 
         try {
-            return provider.canonicalize(mPackageName, mFeatureId, url);
+            final UriResultListener resultListener = new UriResultListener();
+            provider.canonicalizeAsync(mPackageName, mFeatureId, url,
+                    new RemoteCallback(resultListener));
+            resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            return resultListener.result;
         } catch (RemoteException e) {
             // Arbitrary and not worth documenting, as Activity
             // Manager will kill this process shortly anyway.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ae12de0..c6e84b7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3495,13 +3495,23 @@
      * <dl>
      *  <dt> {@link #WINDOW_SERVICE} ("window")
      *  <dd> The top-level window manager in which you can place custom
-     *  windows.  The returned object is a {@link android.view.WindowManager}.
+     *  windows.  The returned object is a {@link android.view.WindowManager}. Must only be obtained
+     *  from a visual context such as Activity or a Context created with
+     *  {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+     *  visual bounds of an area on screen.
      *  <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")
      *  <dd> A {@link android.view.LayoutInflater} for inflating layout resources
-     *  in this context.
+     *  in this context. Must only be obtained from a visual context such as Activity or a Context
+     *  created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the
+     *  configuration and visual bounds of an area on screen.
      *  <dt> {@link #ACTIVITY_SERVICE} ("activity")
      *  <dd> A {@link android.app.ActivityManager} for interacting with the
      *  global activity state of the system.
+     *  <dt> {@link #WALLPAPER_SERVICE} ("wallpaper")
+     *  <dd> A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this
+     *  context. Must only be obtained from a visual context such as Activity or a Context created
+     *  with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+     *  visual bounds of an area on screen.
      *  <dt> {@link #POWER_SERVICE} ("power")
      *  <dd> A {@link android.os.PowerManager} for controlling power
      *  management.
@@ -5901,6 +5911,8 @@
      * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
      * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
      * @return Returns the {@link Display} object this context is associated with.
+     * @throws UnsupportedOperationException if the method is called on an instance that is not
+     *         associated with any display.
      */
     @Nullable
     public Display getDisplay() {
@@ -5908,6 +5920,17 @@
     }
 
     /**
+     * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by
+     * legacy APIs.
+     * TODO(b/149790106): Fix usages and remove.
+     * @hide
+     */
+    @Nullable
+    public Display getDisplayNoVerify() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Gets the ID of the display this context is associated with.
      *
      * @return display ID associated with this {@link Context}.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f6515e8..e5381ea 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1003,6 +1003,12 @@
         return mBase.getDisplay();
     }
 
+    /** @hide */
+    @Override
+    public @Nullable Display getDisplayNoVerify() {
+        return mBase.getDisplayNoVerify();
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 4658ba1..37643da 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -45,7 +45,7 @@
     public String getType(Uri url) throws RemoteException;
 
     /**
-     * An oneway version of getType. The functionality is exactly the same, except that the
+     * A oneway version of getType. The functionality is exactly the same, except that the
      * call returns immediately, and the resulting type is returned when available via
      * a binder callback.
      */
@@ -126,6 +126,14 @@
     public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
             throws RemoteException;
 
+    /**
+     * A oneway version of canonicalize. The functionality is exactly the same, except that the
+     * call returns immediately, and the resulting type is returned when available via
+     * a binder callback.
+     */
+    void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+            RemoteCallback callback) throws RemoteException;
+
     public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
             throws RemoteException;
 
@@ -162,4 +170,5 @@
     static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
     static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
     int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
+    int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
 }
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 6ad7fae..ce26cb0 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -28,21 +28,21 @@
  * @hide
  */
 public final class DeviceProductInfo implements Parcelable {
-    final private String mName;
-    final private String mManufacturerPnpId;
-    final private String mProductId;
-    final private Integer mModelYear;
-    final private ManufactureDate mManufactureDate;
+    private final String mName;
+    private final String mManufacturerPnpId;
+    private final String mProductId;
+    private final Integer mModelYear;
+    private final ManufactureDate mManufactureDate;
 
     public DeviceProductInfo(
             String name,
             String manufacturerPnpId,
-            String productCode,
+            String productId,
             Integer modelYear,
             ManufactureDate manufactureDate) {
         this.mName = name;
         this.mManufacturerPnpId = manufacturerPnpId;
-        this.mProductId = productCode;
+        this.mProductId = productId;
         this.mModelYear = modelYear;
         this.mManufactureDate = manufactureDate;
     }
@@ -158,8 +158,8 @@
      * @hide
      */
     public static class ManufactureDate implements Parcelable {
-        final private Integer mWeek;
-        final private Integer mYear;
+        private final Integer mWeek;
+        private final Integer mYear;
 
         public ManufactureDate(Integer week, Integer year) {
             mWeek = week;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index be22458..d60820e 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import android.annotation.NonNull;
@@ -33,6 +35,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
 import android.provider.Settings;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -614,7 +617,8 @@
 
             // On TV, reboot quiescently if the screen is off
             if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
-                if (context.getDisplay().getState() != Display.STATE_ON) {
+                DisplayManager dm = context.getSystemService(DisplayManager.class);
+                if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) {
                     reason += ",quiescent";
                 }
             }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3faaff7..e8af564 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -42,6 +42,7 @@
 import android.os.strictmode.ExplicitGcViolation;
 import android.os.strictmode.FileUriExposedViolation;
 import android.os.strictmode.ImplicitDirectBootViolation;
+import android.os.strictmode.IncorrectContextUseViolation;
 import android.os.strictmode.InstanceCountViolation;
 import android.os.strictmode.IntentReceiverLeakedViolation;
 import android.os.strictmode.LeakedClosableViolation;
@@ -250,6 +251,7 @@
             DETECT_VM_UNTAGGED_SOCKET,
             DETECT_VM_NON_SDK_API_USAGE,
             DETECT_VM_IMPLICIT_DIRECT_BOOT,
+            DETECT_VM_INCORRECT_CONTEXT_USE,
             PENALTY_GATHER,
             PENALTY_LOG,
             PENALTY_DIALOG,
@@ -289,6 +291,8 @@
     private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10;
     /** @hide */
     private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
+    /** @hide */
+    private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
 
     /** @hide */
     private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -871,6 +875,9 @@
                 if (targetSdk >= Build.VERSION_CODES.Q) {
                     detectCredentialProtectedWhileLocked();
                 }
+                if (targetSdk >= Build.VERSION_CODES.R) {
+                    detectIncorrectContextUse();
+                }
 
                 // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
                 // TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1028,6 +1035,32 @@
             }
 
             /**
+             * Detect attempts to invoke a method on a {@link Context} that is not suited for such
+             * operation.
+             * <p>An example of this is trying to obtain an instance of visual service (e.g.
+             * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
+             * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
+             * therefore can report incorrect metrics or resources.
+             * @see Context#getDisplay()
+             * @see Context#getSystemService(String)
+             * @hide
+             */
+            @TestApi
+            public @NonNull Builder detectIncorrectContextUse() {
+                return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
+            }
+
+            /**
+             * Disable detection of incorrect context use.
+             * TODO(b/149790106): Fix usages and remove.
+             * @hide
+             */
+            @TestApi
+            public @NonNull Builder permitIncorrectContextUse() {
+                return disable(DETECT_VM_INCORRECT_CONTEXT_USE);
+            }
+
+            /**
              * Crashes the whole process on violation. This penalty runs at the end of all enabled
              * penalties so you'll still get your logging or other violations before the process
              * dies.
@@ -2057,6 +2090,11 @@
     }
 
     /** @hide */
+    public static boolean vmIncorrectContextUseEnabled() {
+        return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0;
+    }
+
+    /** @hide */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
     }
@@ -2130,6 +2168,11 @@
         onVmPolicyViolation(new ImplicitDirectBootViolation());
     }
 
+    /** @hide */
+    public static void onIncorrectContextUsed(String message, Throwable originStack) {
+        onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
+    }
+
     /** Assume locked until we hear otherwise */
     private static volatile boolean sUserKeyUnlocked = false;
 
diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
new file mode 100644
index 0000000..647db17
--- /dev/null
+++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.strictmode;
+
+import android.content.Context;
+
+/**
+ * Incorrect usage of {@link Context}, such as obtaining a visual service from non-visual
+ * {@link Context} instance.
+ * @see Context#getSystemService(String)
+ * @see Context#getDisplayNoVerify()
+ * @hide
+ */
+public final class IncorrectContextUseViolation extends Violation {
+
+    /** @hide */
+    public IncorrectContextUseViolation(String message, Throwable originStack) {
+        super(message);
+        initCause(originStack);
+    }
+}
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index cb20db9..b23d0cd 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -15,11 +15,14 @@
  */
 package android.service.controls;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
@@ -51,6 +54,20 @@
     @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_CONTROLS =
             "android.service.controls.ControlsProviderService";
+
+    /**
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ADD_CONTROL =
+            "android.service.controls.action.ADD_CONTROL";
+
+    /**
+     * @hide
+     */
+    public static final String EXTRA_CONTROL =
+            "android.service.controls.extra.CONTROL";
+
     /**
      * @hide
      */
@@ -325,6 +342,33 @@
         }
     }
 
+    /**
+     * Request SystemUI to prompt the user to add a control to favorites.
+     *
+     * @param context A context
+     * @param componentName Component name of the {@link ControlsProviderService}
+     * @param control A stateless control to show to the user
+     */
+    public static void requestAddControl(@NonNull Context context,
+            @NonNull ComponentName componentName,
+            @NonNull Control control) {
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(componentName);
+        Preconditions.checkNotNull(control);
+        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+                context.getResources().getString(
+                        com.android.internal.R.string.config_systemUIServiceComponent));
+        Intent intent = new Intent(ACTION_ADD_CONTROL);
+        intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName);
+        intent.setPackage(sysuiComponent.getPackageName());
+        if (isStatelessControl(control)) {
+            intent.putExtra(EXTRA_CONTROL, control);
+        } else {
+            intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build());
+        }
+        context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS);
+    }
+
     private static class SubscriptionAdapter extends IControlsSubscription.Stub {
         final Subscription mSubscription;
 
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 56683dd..d40f505 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -101,7 +101,7 @@
     @Override
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         applyDefaultToken(params);
-        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
+        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow);
         mIsViewAdded = true;
         mLastView = view;
         mLastParams = (WindowManager.LayoutParams) params;
@@ -158,7 +158,7 @@
 
     @Override
     public Display getDefaultDisplay() {
-        return mContext.getDisplay();
+        return mContext.getDisplayNoVerify();
     }
 
     @Override
@@ -240,7 +240,7 @@
     private Rect getMaximumBounds() {
         // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea
         //  bound after displayArea feature is finished.
-        final Display display = mContext.getDisplay();
+        final Display display = mContext.getDisplayNoVerify();
         final Point displaySize = new Point();
         display.getRealSize(displaySize);
         return new Rect(0, 0, displaySize.x, displaySize.y);
diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING
new file mode 100644
index 0000000..4cb595b
--- /dev/null
+++ b/core/java/com/android/internal/content/om/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.content."
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 69b1609..633d684 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -210,7 +210,7 @@
     }
 
     private boolean isContentRectWithinBounds() {
-        mContext.getDisplay().getRealSize(mDisplaySize);
+        mContext.getDisplayNoVerify().getRealSize(mDisplaySize);
         mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
 
         return intersectsClosed(mContentRectOnScreen, mScreenRect)
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 13425e5..cfb2bf9 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -77,10 +77,7 @@
             final Point size = new Point();
             final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
             try {
-                final Display display = context.getDisplay();
-                final int displayId = display != null
-                        ? display.getDisplayId()
-                        : Display.DEFAULT_DISPLAY;
+                final int displayId = context.getDisplayId();
                 wm.getInitialDisplaySize(displayId, size);
                 return size.x < size.y ?
                         Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index f03f427..8bb4d50 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -47,28 +47,32 @@
 static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
                                        const char* field_signature) {
     jfieldID res = env->GetFieldID(clazz, field_name, field_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+                        field_signature);
     return res;
 }
 
 static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
                                          const char* method_signature) {
     jmethodID res = env->GetMethodID(clazz, method_name, method_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name,
+                        method_signature);
     return res;
 }
 
 static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
                                              const char* field_signature) {
     jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+                        field_signature);
     return res;
 }
 
 static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
                                                const char* method_signature) {
     jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature);
-    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name);
+    LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s",
+                        method_name, method_signature);
     return res;
 }
 
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index e476c52..e65a2ab 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -50,6 +50,9 @@
 
   // Integer representation of conversation bit flags.
   optional int32 conversation_flags = 6;
+
+  // The phone number of the contact.
+  optional string contact_phone_number = 7;
 }
 
 // On disk data of events.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ddfc4b8..814b8ac 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3084,8 +3084,7 @@
 
     <!-- Allows SystemUI to request third party controls.
          <p>Should only be requested by the System and required by
-         ControlsService declarations.
-         @hide
+         {@link android.service.controls.ControlsProviderService} declarations.
     -->
     <permission android:name="android.permission.BIND_CONTROLS"
         android:protectionLevel="signature" />
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 48049b4..4f221d0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -781,9 +781,6 @@
 
     <dimen name="chooser_action_button_icon_size">18dp</dimen>
 
-    <!-- Assistant handles -->
-    <dimen name="assist_handle_shadow_radius">2dp</dimen>
-
     <!-- For Waterfall Display -->
     <dimen name="waterfall_display_left_edge_size">0px</dimen>
     <dimen name="waterfall_display_top_edge_size">0px</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1c9e2cd..e21dee6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3840,9 +3840,6 @@
   <!-- For App Standby -->
   <java-symbol type="string" name="as_app_forced_to_restricted_bucket" />
 
-  <!-- Assistant handles -->
-  <java-symbol type="dimen" name="assist_handle_shadow_radius" />
-
   <!-- For Waterfall Display -->
   <java-symbol type="dimen" name="waterfall_display_left_edge_size" />
   <java-symbol type="dimen" name="waterfall_display_top_edge_size" />
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 78c4420..1737bd0 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -234,4 +234,12 @@
         assertThat(type).isNull();
         assertThat(end).isLessThan(start + 5000);
     }
+
+    @Test
+    public void testCanonicalize() {
+        Uri canonical = mResolver.canonicalize(
+                Uri.parse("content://android.content.FakeProviderRemote/something"));
+        assertThat(canonical).isEqualTo(
+                Uri.parse("content://android.content.FakeProviderRemote/canonical"));
+    }
 }
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index f074233..f0997a6 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -16,11 +16,13 @@
 
 package android.content;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.ActivityThread;
+import android.hardware.display.DisplayManager;
 import android.os.UserHandle;
-import android.view.WindowManager;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -45,17 +47,16 @@
         final Context testContext =
                 InstrumentationRegistry.getInstrumentation().getTargetContext();
 
-        assertEquals(testContext.getDisplay().getDisplayId(), testContext.getDisplayId());
+        assertEquals(testContext.getDisplayNoVerify().getDisplayId(), testContext.getDisplayId());
     }
 
-    // TODO(b/128338354): Re-visit this test after introducing WindowContext
     @Test
     public void testDisplayIdForDefaultDisplayContext() {
         final Context testContext =
                 InstrumentationRegistry.getInstrumentation().getTargetContext();
-        final WindowManager wm = testContext.getSystemService(WindowManager.class);
+        final DisplayManager dm = testContext.getSystemService(DisplayManager.class);
         final Context defaultDisplayContext =
-                testContext.createDisplayContext(wm.getDefaultDisplay());
+                testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
 
         assertEquals(defaultDisplayContext.getDisplay().getDisplayId(),
                 defaultDisplayContext.getDisplayId());
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 7b9bdbc..1d7ba5d 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -54,4 +54,10 @@
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         return 0;
     }
+
+    @Override
+    public Uri canonicalize(Uri uri) {
+        return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
+                .appendPath("canonical").build();
+    }
 }
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 78c88d7..4c2ca7e 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -25,9 +25,13 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.Manifest;
 import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -44,6 +48,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -59,6 +64,11 @@
 @RunWith(AndroidJUnit4.class)
 public class ControlProviderServiceTest {
 
+    private static final ComponentName TEST_SYSUI_COMPONENT =
+            ComponentName.unflattenFromString("sysui/.test.cls");
+    private static final ComponentName TEST_COMPONENT =
+            ComponentName.unflattenFromString("test.pkg/.test.cls");
+
     private IBinder mToken = new Binder();
     @Mock
     private IControlsActionCallback.Stub mActionCallback;
@@ -66,6 +76,12 @@
     private IControlsSubscriber.Stub mSubscriber;
     @Mock
     private IIntentSender mIIntentSender;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private Context mContext;
+    @Captor
+    private ArgumentCaptor<Intent> mIntentArgumentCaptor;
 
     private PendingIntent mPendingIntent;
     private FakeControlsProviderService mControlsProviderService;
@@ -81,6 +97,10 @@
         when(mSubscriber.asBinder()).thenCallRealMethod();
         when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
 
+        when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent))
+                .thenReturn(TEST_SYSUI_COMPONENT.flattenToString());
+        when(mContext.getResources()).thenReturn(mResources);
+
         Bundle b = new Bundle();
         b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken);
         Intent intent = new Intent();
@@ -223,6 +243,21 @@
                 ControlAction.RESPONSE_OK);
     }
 
+    @Test
+    public void testRequestAdd() {
+        Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+        ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control);
+
+        verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(),
+                eq(Manifest.permission.BIND_CONTROLS));
+        Intent intent = mIntentArgumentCaptor.getValue();
+        assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction());
+        assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage());
+        assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME));
+        assertTrue(equals(control,
+                intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
+    }
+
     private static boolean equals(Control c1, Control c2) {
         if (c1 == c2) return true;
         if (c1 == null || c2 == null) return false;
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 7c78bce..7f0e0d2 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -61,7 +61,7 @@
                 .setName("testSurface")
                 .build();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplay());
+            ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify());
             try {
                 viewRootImpl.setView(new TextView(mContext), new LayoutParams(), null);
             } catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 24fe2a0..7737b1a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -110,7 +110,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             Context context = InstrumentationRegistry.getTargetContext();
             // cannot mock ViewRootImpl since it's final.
-            mViewRoot = new ViewRootImpl(context, context.getDisplay());
+            mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
             try {
                 mViewRoot.setView(new TextView(context), new LayoutParams(), null);
             } catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 5e9e2f0..754c679 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -77,7 +77,8 @@
         instrumentation.runOnMainSync(() -> {
             final Context context = instrumentation.getTargetContext();
             // cannot mock ViewRootImpl since it's final.
-            final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+            final ViewRootImpl viewRootImpl = new ViewRootImpl(context,
+                    context.getDisplayNoVerify());
             try {
                 viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
             } catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index df6ed8c..e2adbcc 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -61,7 +61,7 @@
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mViewRootImpl = new ViewRootImplAccessor(
-                    new ViewRootImpl(mContext, mContext.getDisplay()));
+                    new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
         });
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index cdcf23f..3e40466 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -53,7 +53,10 @@
 
     @Test
     public void testDecorContextWithDefaultDisplay() {
-        DecorContext context = new DecorContext(mContext.getApplicationContext(), mContext);
+        Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY,
+                new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+        DecorContext context = new DecorContext(mContext.getApplicationContext(),
+                mContext.createDisplayContext(defaultDisplay));
 
         assertDecorContextDisplay(DEFAULT_DISPLAY, context);
     }
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 914c046..56d951c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -150,11 +150,7 @@
     AHardwareBuffer_Desc bufferDesc;
     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
     SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace);
-
-    // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
-    const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
-    const size_t rowBytes = info.bytesPerPixel() * bufferStride;
-    return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
+    return createFrom(hardwareBuffer, info, bufferDesc, palette);
 }
 
 sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType,
@@ -164,8 +160,14 @@
     AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
     SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height,
                                          colorType, alphaType, colorSpace);
+    return createFrom(hardwareBuffer, info, bufferDesc, palette);
+}
 
-    const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride;
+sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
+                                 const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) {
+    // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
+    const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
+    const size_t rowBytes = info.bytesPerPixel() * bufferStride;
     return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
 }
 #endif
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 3bfb780..b8b5994 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -169,6 +169,12 @@
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
     Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes,
            BitmapPalette palette);
+
+    // Common code for the two public facing createFrom(AHardwareBuffer*, ...)
+    // methods.
+    // bufferDesc is only used to compute rowBytes.
+    static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
+                                    const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette);
 #endif
 
     virtual ~Bitmap();
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 085602c..6006d50 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -27,22 +27,6 @@
 public abstract class LocationManagerInternal {
 
     /**
-     * Requests that a provider change its allowed state. A provider may or may not honor this
-     * request, and if the provider does change its state as a result, that may happen
-     * asynchronously after some delay.
-     *
-     * <p>Setting a provider's state to allowed implies that any consents or terms and conditions
-     * that may be necessary to allow the provider are agreed to. Setting a providers state to
-     * disallowed implies that any consents or terms and conditions have their agreement revoked.
-     *
-     * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
-     * @param allowed  Whether the location provider is being requested to allow or disallow
-     *                 itself
-     * @throws IllegalArgumentException if provider is null
-     */
-    public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed);
-
-    /**
      * Returns true if the given provider is enabled for the given user.
      *
      * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index b7817ff..4246c6c 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -37,6 +37,4 @@
 
     @UnsupportedAppUsage
     oneway void sendExtraCommand(String command, in Bundle extras);
-
-    oneway void requestSetAllowed(boolean allowed);
 }
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 49fcaab..9cc30d0 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -17,7 +17,6 @@
     method @Deprecated protected int onGetStatus(android.os.Bundle);
     method @Deprecated protected long onGetStatusUpdateTime();
     method protected void onInit();
-    method protected void onRequestSetAllowed(boolean);
     method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
     method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
     method public void reportLocation(android.location.Location);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index bd29d8a..d3fb58f 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -315,17 +315,6 @@
         return false;
     }
 
-    /**
-     * Invoked when the system wishes to request that the provider sets its allowed state as
-     * desired. This implies that the caller is providing/retracting consent for any terms and
-     * conditions or consents associated with the provider.
-     *
-     * <p>It is generally only necessary to override this function if the provider has some barriers
-     * or gates for enabling/disabling itself, in which case this function should handle those
-     * appropriately. A provider that is always allowed has no need to override this function.
-     */
-    protected void onRequestSetAllowed(boolean allowed) {}
-
     private final class Service extends ILocationProvider.Stub {
 
         @Override
@@ -356,10 +345,5 @@
         public void sendExtraCommand(String command, Bundle extras) {
             onSendExtraCommand(command, extras);
         }
-
-        @Override
-        public void requestSetAllowed(boolean allowed) {
-            onRequestSetAllowed(allowed);
-        }
     }
 }
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index cc35f14..d803311 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -37,7 +37,9 @@
      *
      * @param schemeUuid The DRM scheme's UUID.
      * @return The initialization data for the scheme, or null if the scheme is not supported.
+     * @deprecated Use {@link #getSchemeInitDataCount} and {@link #getSchemeInitDataAt} instead.
      */
+    @Deprecated
     public abstract SchemeInitData get(UUID schemeUuid);
 
     /**
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index abc7e0b..f2b4db1 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1959,7 +1959,7 @@
      * If this codec is to be used with {@link LinearBlock} and/or {@link
      * GraphicBlock}, pass this flag.
      * <p>
-     * When this flag is set, the following APIs throw IllegalStateException.
+     * When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}.
      * <ul>
      * <li>{@link #getInputBuffer}
      * <li>{@link #getInputImage}
@@ -1986,6 +1986,12 @@
     public @interface ConfigureFlag {}
 
     /**
+     * Thrown when the codec is configured for block model and an incompatible API is called.
+     */
+    public class IncompatibleWithBlockModelException extends RuntimeException {
+    }
+
+    /**
      * Configures a component.
      *
      * @param format The format of the input data (decoder) or the desired
@@ -2526,7 +2532,7 @@
         throws CryptoException {
         synchronized(mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.remove(index);
@@ -2778,7 +2784,7 @@
             int flags) throws CryptoException {
         synchronized(mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
             invalidateByteBuffer(mCachedInputBuffers, index);
             mDequeuedInputBuffers.remove(index);
@@ -2813,7 +2819,7 @@
     public final int dequeueInputBuffer(long timeoutUs) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         int res = native_dequeueInputBuffer(timeoutUs);
@@ -2848,7 +2854,7 @@
         public boolean isMappable() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The linear block is invalid");
                 }
                 return mMappable;
             }
@@ -2867,10 +2873,10 @@
         public @NonNull ByteBuffer map() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The linear block is invalid");
                 }
                 if (!mMappable) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The linear block is not mappable");
                 }
                 if (mMapped == null) {
                     mMapped = native_map();
@@ -2896,7 +2902,7 @@
         public void recycle() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The linear block is invalid");
                 }
                 if (mMapped != null) {
                     mMapped.setAccessible(false);
@@ -3002,7 +3008,7 @@
         public boolean isMappable() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The graphic block is invalid");
                 }
                 return mMappable;
             }
@@ -3021,10 +3027,10 @@
         public @NonNull Image map() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The graphic block is invalid");
                 }
                 if (!mMappable) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The graphic block is not mappable");
                 }
                 if (mMapped == null) {
                     mMapped = native_map();
@@ -3050,7 +3056,7 @@
         public void recycle() {
             synchronized (mLock) {
                 if (!mValid) {
-                    throw new IllegalStateException();
+                    throw new IllegalStateException("The graphic block is invalid");
                 }
                 if (mMapped != null) {
                     mMapped.close();
@@ -3127,8 +3133,9 @@
             if (buffer == null) {
                 buffer = new GraphicBlock();
             }
-            if (width < 0 || height < 0) {
-                throw new IllegalArgumentException();
+            if (width <= 0 || height <= 0) {
+                throw new IllegalArgumentException(
+                        "non-positive width or height: " + width + "x" + height);
             }
             synchronized (buffer.mLock) {
                 buffer.native_obtain(width, height, format, usage, codecNames);
@@ -3177,16 +3184,8 @@
          * @param block The linear block object
          * @param offset The byte offset into the input buffer at which the data starts.
          * @param size The number of bytes of valid input data.
-         * @param presentationTimeUs The presentation timestamp in microseconds for this
-         *                           buffer. This is normally the media time at which this
-         *                           buffer should be presented (rendered). When using an output
-         *                           surface, this will be propagated as the {@link
-         *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
-         *                           conversion to nanoseconds).
-         * @param flags A bitmask of flags
-         *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
-         *              While not prohibited, most codecs do not use the
-         *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+         *                   may be null if clear.
          * @return this object
          * @throws IllegalStateException if a buffer is already set
          */
@@ -3194,59 +3193,17 @@
                 @NonNull LinearBlock block,
                 int offset,
                 int size,
-                long presentationTimeUs,
-                @BufferFlag int flags) {
+                @Nullable MediaCodec.CryptoInfo cryptoInfo) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             if (mLinearBlock != null || mGraphicBlock != null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("Cannot set block twice");
             }
             mLinearBlock = block;
             mOffset = offset;
             mSize = size;
-            mPresentationTimeUs = presentationTimeUs;
-            mFlags = flags;
-            return this;
-        }
-
-        /**
-         * Set an encrypted linear block to this queue request. Exactly one
-         * buffer must be set for a queue request before calling {@link #queue}.
-         *
-         * @param block The linear block object
-         * @param offset The byte offset into the input buffer at which the data starts.
-         * @param presentationTimeUs The presentation timestamp in microseconds for this
-         *                           buffer. This is normally the media time at which this
-         *                           buffer should be presented (rendered). When using an output
-         *                           surface, this will be propagated as the {@link
-         *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
-         *                           conversion to nanoseconds).
-         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
-         * @param flags A bitmask of flags
-         *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
-         *              While not prohibited, most codecs do not use the
-         *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
-         * @return this object
-         * @throws IllegalStateException if a buffer is already set
-         */
-        public @NonNull QueueRequest setEncryptedLinearBlock(
-                @NonNull LinearBlock block,
-                int offset,
-                @NonNull MediaCodec.CryptoInfo cryptoInfo,
-                long presentationTimeUs,
-                @BufferFlag int flags) {
-            if (!isAccessible()) {
-                throw new IllegalStateException();
-            }
-            if (mLinearBlock != null || mGraphicBlock != null) {
-                throw new IllegalStateException();
-            }
-            mLinearBlock = block;
-            mOffset = offset;
             mCryptoInfo = cryptoInfo;
-            mPresentationTimeUs = presentationTimeUs;
-            mFlags = flags;
             return this;
         }
 
@@ -3255,45 +3212,72 @@
          * be set for a queue request before calling {@link #queue}.
          *
          * @param block The graphic block object
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setGraphicBlock(
+                @NonNull GraphicBlock block) {
+            if (!isAccessible()) {
+                throw new IllegalStateException("The request is stale");
+            }
+            if (mLinearBlock != null || mGraphicBlock != null) {
+                throw new IllegalStateException("Cannot set block twice");
+            }
+            mGraphicBlock = block;
+            return this;
+        }
+
+        /**
+         * Set timestamp to this queue request.
+         *
          * @param presentationTimeUs The presentation timestamp in microseconds for this
          *                           buffer. This is normally the media time at which this
          *                           buffer should be presented (rendered). When using an output
          *                           surface, this will be propagated as the {@link
          *                           SurfaceTexture#getTimestamp timestamp} for the frame (after
          *                           conversion to nanoseconds).
+         * @return this object
+         */
+        public @NonNull QueueRequest setPresentationTimeUs(long presentationTimeUs) {
+            if (!isAccessible()) {
+                throw new IllegalStateException("The request is stale");
+            }
+            mPresentationTimeUs = presentationTimeUs;
+            return this;
+        }
+
+        /**
+         * Set flags to this queue request.
+         *
          * @param flags A bitmask of flags
          *              {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
          *              While not prohibited, most codecs do not use the
          *              {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
          * @return this object
-         * @throws IllegalStateException if a buffer is already set
          */
-        public @NonNull QueueRequest setGraphicBlock(
-                @NonNull GraphicBlock block,
-                long presentationTimeUs,
-                @BufferFlag int flags) {
+        public @NonNull QueueRequest setFlags(@BufferFlag int flags) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
-            if (mLinearBlock != null || mGraphicBlock != null) {
-                throw new IllegalStateException();
-            }
-            mGraphicBlock = block;
-            mPresentationTimeUs = presentationTimeUs;
             mFlags = flags;
             return this;
         }
 
         /**
-         * Add a integer parameter. See {@link MediaFormat} for the list of
-         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * Add an integer parameter.
+         * See {@link MediaFormat} for an exhaustive list of supported keys with
+         * values of type int, that can also be set with {@link MediaFormat#setInteger}.
+         *
+         * If there was {@link MediaCodec#setParameters}
          * call with the same key which is not processed by the codec yet, the
          * value set from this method will override the unprocessed value.
+         *
+         * @return this object
          */
         public @NonNull QueueRequest setIntegerParameter(
                 @NonNull String key, int value) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             mTuningKeys.add(key);
             mTuningValues.add(Integer.valueOf(value));
@@ -3301,15 +3285,20 @@
         }
 
         /**
-         * Add a long parameter. See {@link MediaFormat} for the list of
-         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * Add a long parameter.
+         * See {@link MediaFormat} for an exhaustive list of supported keys with
+         * values of type long, that can also be set with {@link MediaFormat#setLong}.
+         *
+         * If there was {@link MediaCodec#setParameters}
          * call with the same key which is not processed by the codec yet, the
          * value set from this method will override the unprocessed value.
+         *
+         * @return this object
          */
         public @NonNull QueueRequest setLongParameter(
                 @NonNull String key, long value) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             mTuningKeys.add(key);
             mTuningValues.add(Long.valueOf(value));
@@ -3317,15 +3306,20 @@
         }
 
         /**
-         * Add a float parameter. See {@link MediaFormat} for the list of
-         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * Add a float parameter.
+         * See {@link MediaFormat} for an exhaustive list of supported keys with
+         * values of type float, that can also be set with {@link MediaFormat#setFloat}.
+         *
+         * If there was {@link MediaCodec#setParameters}
          * call with the same key which is not processed by the codec yet, the
          * value set from this method will override the unprocessed value.
+         *
+         * @return this object
          */
         public @NonNull QueueRequest setFloatParameter(
                 @NonNull String key, float value) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             mTuningKeys.add(key);
             mTuningValues.add(Float.valueOf(value));
@@ -3333,15 +3327,20 @@
         }
 
         /**
-         * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of
-         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * Add a {@link ByteBuffer} parameter.
+         * See {@link MediaFormat} for an exhaustive list of supported keys with
+         * values of byte buffer, that can also be set with {@link MediaFormat#setByteBuffer}.
+         *
+         * If there was {@link MediaCodec#setParameters}
          * call with the same key which is not processed by the codec yet, the
          * value set from this method will override the unprocessed value.
+         *
+         * @return this object
          */
         public @NonNull QueueRequest setByteBufferParameter(
                 @NonNull String key, @NonNull ByteBuffer value) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             mTuningKeys.add(key);
             mTuningValues.add(value);
@@ -3349,15 +3348,20 @@
         }
 
         /**
-         * Add a string parameter. See {@link MediaFormat} for the list of
-         * supported tunings. If there was {@link MediaCodec#setParameters}
+         * Add a string parameter.
+         * See {@link MediaFormat} for an exhaustive list of supported keys with
+         * values of type string, that can also be set with {@link MediaFormat#setString}.
+         *
+         * If there was {@link MediaCodec#setParameters}
          * call with the same key which is not processed by the codec yet, the
          * value set from this method will override the unprocessed value.
+         *
+         * @return this object
          */
         public @NonNull QueueRequest setStringParameter(
                 @NonNull String key, @NonNull String value) {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             mTuningKeys.add(key);
             mTuningValues.add(value);
@@ -3369,10 +3373,10 @@
          */
         public void queue() {
             if (!isAccessible()) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The request is stale");
             }
             if (mLinearBlock == null && mGraphicBlock == null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("No block is set");
             }
             setAccessible(false);
             if (mLinearBlock != null) {
@@ -3447,7 +3451,7 @@
     private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>();
 
     /**
-     * Return a clear {@link QueueRequest} object for an input slot index.
+     * Return a {@link QueueRequest} object for an input slot index.
      *
      * @param index input slot index from
      *              {@link Callback#onInputBufferAvailable}
@@ -3459,17 +3463,19 @@
     public @NonNull QueueRequest getQueueRequest(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_BLOCK) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The codec is not configured for block model");
             }
             if (index < 0 || index >= mQueueRequests.size()) {
-                throw new IllegalArgumentException();
+                throw new IndexOutOfBoundsException("Expected range of index: [0,"
+                        + (mQueueRequests.size() - 1) + "]; actual: " + index);
             }
             QueueRequest request = mQueueRequests.get(index);
             if (request == null) {
-                throw new IllegalArgumentException();
+                throw new IllegalArgumentException("Unavailable index: " + index);
             }
             if (!request.isAccessible()) {
-                throw new IllegalArgumentException();
+                throw new IllegalArgumentException(
+                        "The request is stale at index " + index);
             }
             return request.clear();
         }
@@ -3529,7 +3535,7 @@
             @NonNull BufferInfo info, long timeoutUs) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         int res = native_dequeueOutputBuffer(info, timeoutUs);
@@ -3644,7 +3650,8 @@
                     frame.clear();
                     break;
                 default:
-                    throw new IllegalStateException();
+                    throw new IllegalStateException(
+                            "Unrecognized buffer mode: " + mBufferMode);
             }
         }
         releaseOutputBuffer(
@@ -3910,7 +3917,7 @@
     public ByteBuffer[] getInputBuffers() {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
             if (mCachedInputBuffers == null) {
                 throw new IllegalStateException();
@@ -3946,7 +3953,7 @@
     public ByteBuffer[] getOutputBuffers() {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
             if (mCachedOutputBuffers == null) {
                 throw new IllegalStateException();
@@ -3978,7 +3985,7 @@
     public ByteBuffer getInputBuffer(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         ByteBuffer newBuffer = getBuffer(true /* input */, index);
@@ -4012,7 +4019,7 @@
     public Image getInputImage(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         Image newImage = getImage(true /* input */, index);
@@ -4046,7 +4053,7 @@
     public ByteBuffer getOutputBuffer(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         ByteBuffer newBuffer = getBuffer(false /* input */, index);
@@ -4079,7 +4086,7 @@
     public Image getOutputImage(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_LEGACY) {
-                throw new IllegalStateException();
+                throw new IncompatibleWithBlockModelException();
             }
         }
         Image newImage = getImage(false /* input */, index);
@@ -4106,7 +4113,7 @@
          */
         public @Nullable LinearBlock getLinearBlock() {
             if (mGraphicBlock != null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("This output frame is not linear");
             }
             return mLinearBlock;
         }
@@ -4118,7 +4125,7 @@
          */
         public @Nullable GraphicBlock getGraphicBlock() {
             if (mLinearBlock != null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("This output frame is not graphic");
             }
             return mGraphicBlock;
         }
@@ -4139,7 +4146,7 @@
 
         /**
          * Returns a read-only {@link MediaFormat} for this frame. The returned
-         * object is valid only while the client is holding the output frame.
+         * object is valid only until the client calls {@link MediaCodec#releaseOutputBuffer}.
          */
         public @NonNull MediaFormat getFormat() {
             return mFormat;
@@ -4151,7 +4158,7 @@
          * Client can find out what the change is by querying {@link MediaFormat}
          * object returned from {@link #getFormat}.
          */
-        public void getChangedKeys(@NonNull Set<String> keys) {
+        public void retrieveChangedKeys(@NonNull Set<String> keys) {
             keys.clear();
             keys.addAll(mChangedKeys);
         }
@@ -4211,17 +4218,19 @@
     public @NonNull OutputFrame getOutputFrame(int index) {
         synchronized (mBufferLock) {
             if (mBufferMode != BUFFER_MODE_BLOCK) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("The codec is not configured for block model");
             }
             if (index < 0 || index >= mOutputFrames.size()) {
-                throw new IllegalArgumentException();
+                throw new IndexOutOfBoundsException("Expected range of index: [0,"
+                        + (mQueueRequests.size() - 1) + "]; actual: " + index);
             }
             OutputFrame frame = mOutputFrames.get(index);
             if (frame == null) {
-                throw new IllegalArgumentException();
+                throw new IllegalArgumentException("Unavailable index: " + index);
             }
             if (!frame.isAccessible()) {
-                throw new IllegalArgumentException();
+                throw new IllegalArgumentException(
+                        "The output frame is stale at index " + index);
             }
             if (!frame.isLoaded()) {
                 native_getOutputFrame(frame, index);
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index e0ca1ab..a38091d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -97,7 +97,7 @@
 
         final Resources res = context.getResources();
         final DisplayMetrics metrics = new DisplayMetrics();
-        context.getDisplay().getRealMetrics(metrics);
+        context.getDisplayNoVerify().getRealMetrics(metrics);
 
         final int currentDensity = metrics.densityDpi;
         int currentDensityIndex = -1;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5458676e..e2410fe 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -696,8 +696,7 @@
         <provider
             android:name="com.android.keyguard.clock.ClockOptionsProvider"
             android:authorities="com.android.keyguard.clock"
-            android:enabled="false"
-            android:exported="false"
+            android:exported="true"
             android:grantUriPermissions="true">
         </provider>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index e475ef1..6f06f69 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -51,7 +51,7 @@
 public class KeyguardClockSwitch extends RelativeLayout {
 
     private static final String TAG = "KeyguardClockSwitch";
-    private static final boolean CUSTOM_CLOCKS_ENABLED = false;
+    private static final boolean CUSTOM_CLOCKS_ENABLED = true;
 
     /**
      * Animation fraction when text is transitioned to/from bold.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 61caf3b..241f96e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -149,7 +149,7 @@
                             new WindowlessWindowManager(context.getResources().getConfiguration(),
                                     surfaceControl, input);
                     mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context,
-                            context.getDisplay(), windowlessWindowManager);
+                            context.getDisplayNoVerify(), windowlessWindowManager);
                     WindowManager.LayoutParams layoutParams =
                             new WindowManager.LayoutParams(
                                     surfaceControl.getWidth(),
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9cd4aec..0367464 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -149,8 +149,6 @@
         LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
 
         addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor));
-        addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor));
-        addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor));
 
         // Store the size of the display for generation of clock preview.
         DisplayMetrics dm = res.getDisplayMetrics();
@@ -211,7 +209,8 @@
         return mContentObserver;
     }
 
-    private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) {
+    @VisibleForTesting
+    void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) {
         ClockPlugin plugin = pluginSupplier.get();
         mPreviewClocks.addClockPlugin(plugin);
         mBuiltinClocks.add(pluginSupplier);
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 8503396..85ce313 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -57,7 +57,6 @@
         mPaint.setStyle(Paint.Style.STROKE);
         mPaint.setStrokeCap(Paint.Cap.ROUND);
         mPaint.setStrokeWidth(getStrokePx());
-        setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint);
 
         final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
         final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
@@ -118,14 +117,8 @@
         // Handle color is same as home handle color.
         int color = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
                 mLightColor, mDarkColor);
-        // Shadow color is inverse of handle color.
-        int shadowColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
-                mDarkColor, mLightColor);
         if (mPaint.getColor() != color) {
             mPaint.setColor(color);
-            mPaint.setShadowLayer(/** radius */ getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.assist_handle_shadow_radius), /** shadowDx */ 0,
-                    /** shadowDy */ 0, /** color */ shadowColor);
             if (getVisibility() == VISIBLE && getAlpha() > 0) {
                 invalidate();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index f4157f2..8625d63 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -126,6 +126,13 @@
     internal var startAction: () -> Unit = ::startInternal
 
     /**
+     * Action to run when [cancel] is called. This can be changed by
+     * [PhysicsAnimatorTestUtils.prepareForTest] to cancel animations from the main thread, which
+     * is required.
+     */
+    internal var cancelAction: (Set<FloatPropertyCompat<in T>>) -> Unit = ::cancelInternal
+
+    /**
      * Springs a property to the given value, using the provided configuration settings.
      *
      * Springs are used when you know the exact value to which you want to animate. They can be
@@ -429,10 +436,13 @@
                         max = max(currentValue, this.max)
                     }
 
-                    // Apply the configuration and start the animation. Since flings can't be
-                    // redirected while in motion, cancel it first.
+                    // Flings can't be updated to a new position while maintaining velocity, because
+                    // we're using the explicitly provided start velocity. Cancel any flings (or
+                    // springs) on this property before flinging.
+                    cancel(animatedProperty)
+
+                    // Apply the configuration and start the animation.
                     getFlingAnimation(animatedProperty)
-                            .also { it.cancel() }
                             .also { flingConfig.applyToAnimation(it) }
                             .start()
                 }
@@ -707,11 +717,26 @@
         return springConfigs.keys.union(flingConfigs.keys)
     }
 
+    /**
+     * Cancels the given properties. This is typically called immediately by [cancel], unless this
+     * animator is under test.
+     */
+    internal fun cancelInternal(properties: Set<FloatPropertyCompat<in T>>) {
+        for (property in properties) {
+            flingAnimations[property]?.cancel()
+            springAnimations[property]?.cancel()
+        }
+    }
+
     /** Cancels all in progress animations on all properties. */
     fun cancel() {
-        for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) {
-            dynamicAnim.cancel()
-        }
+        cancelAction(flingAnimations.keys)
+        cancelAction(springAnimations.keys)
+    }
+
+    /** Cancels in progress animations on the provided properties only. */
+    fun cancel(vararg properties: FloatPropertyCompat<in T>) {
+        cancelAction(properties.toSet())
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
index 965decd..c50eeac 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
@@ -363,8 +363,12 @@
         private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>()
         private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>()
 
+        /** Whether we're currently in the middle of executing startInternal(). */
+        private var currentlyRunningStartInternal = false
+
         init {
             animator.startAction = ::startForTest
+            animator.cancelAction = ::cancelForTest
         }
 
         internal fun addTestEndListener(listener: PhysicsAnimator.EndListener<T>) {
@@ -437,7 +441,29 @@
                     }
                 })
 
+                currentlyRunningStartInternal = true
                 animator.startInternal()
+                currentlyRunningStartInternal = false
+                unblockLatch.countDown()
+            }
+
+            unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS)
+        }
+
+        private fun cancelForTest(properties: Set<FloatPropertyCompat<in T>>) {
+            // If this was called from startInternal, we are already on the animation thread, and
+            // should just call cancelInternal rather than posting it. If we post it, the
+            // cancellation will occur after the rest of startInternal() and we'll immediately
+            // cancel the animation we worked so hard to start!
+            if (currentlyRunningStartInternal) {
+                animator.cancelInternal(properties)
+                return
+            }
+
+            val unblockLatch = CountDownLatch(1)
+
+            animationThreadHandler.post {
+                animator.cancelInternal(properties)
                 unblockLatch.countDown()
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
new file mode 100644
index 0000000..2276ba1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2020 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.util.magnetictarget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.PointF
+import android.os.Handler
+import android.os.UserHandle
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.provider.Settings
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.util.animation.PhysicsAnimator
+import kotlin.math.hypot
+
+/**
+ * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic
+ * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless
+ * they're moved away or released. Releasing objects inside a magnetic target typically performs an
+ * action on the object.
+ *
+ * MagnetizedObject also supports flinging to targets, which will result in the object being pulled
+ * into the target and released as if it was dragged into it.
+ *
+ * To use this class, either construct an instance with an object of arbitrary type, or use the
+ * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set
+ * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents
+ * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the
+ * event consumed by the MagnetizedObject and don't move the object unless it begins returning false
+ * again.
+ *
+ * @param context Context, used to retrieve a Vibrator instance for vibration effects.
+ * @param underlyingObject The actual object that we're magnetizing.
+ * @param xProperty Property that sets the x value of the object's position.
+ * @param yProperty Property that sets the y value of the object's position.
+ */
+abstract class MagnetizedObject<T : Any>(
+    val context: Context,
+
+    /** The actual object that is animated. */
+    val underlyingObject: T,
+
+    /** Property that gets/sets the object's X value. */
+    val xProperty: FloatPropertyCompat<in T>,
+
+    /** Property that gets/sets the object's Y value. */
+    val yProperty: FloatPropertyCompat<in T>
+) {
+
+    /** Return the width of the object. */
+    abstract fun getWidth(underlyingObject: T): Float
+
+    /** Return the height of the object. */
+    abstract fun getHeight(underlyingObject: T): Float
+
+    /**
+     * Fill the provided array with the location of the top-left of the object, relative to the
+     * entire screen. Compare to [View.getLocationOnScreen].
+     */
+    abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray)
+
+    /** Methods for listening to events involving a magnetized object.  */
+    interface MagnetListener {
+
+        /**
+         * Called when touch events move within the magnetic field of a target, causing the
+         * object to animate to the target and become 'stuck' there. The animation happens
+         * automatically here - you should not move the object. You can, however, change its state
+         * to indicate to the user that it's inside the target and releasing it will have an effect.
+         *
+         * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call
+         * to [onUnstuckFromTarget] or [onReleasedInTarget].
+         *
+         * @param target The target that the object is now stuck to.
+         */
+        fun onStuckToTarget(target: MagneticTarget)
+
+        /**
+         * Called when the object is no longer stuck to a target. This means that either touch
+         * events moved outside of the magnetic field radius, or that a forceful fling out of the
+         * target was detected.
+         *
+         * The object won't be automatically animated out of the target, since you're responsible
+         * for moving the object again. You should move it (or animate it) using your own
+         * movement/animation logic.
+         *
+         * Reverse any effects applied in [onStuckToTarget] here.
+         *
+         * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event
+         * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing
+         * and [maybeConsumeMotionEvent] is now returning false.
+         *
+         * @param target The target that this object was just unstuck from.
+         * @param velX The X velocity of the touch gesture when it exited the magnetic field.
+         * @param velY The Y velocity of the touch gesture when it exited the magnetic field.
+         * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that
+         * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude
+         * that the user wants to un-stick the object despite no touch events occurring outside of
+         * the magnetic field radius.
+         */
+        fun onUnstuckFromTarget(
+            target: MagneticTarget,
+            velX: Float,
+            velY: Float,
+            wasFlungOut: Boolean
+        )
+
+        /**
+         * Called when the object is released inside a target, or flung towards it with enough
+         * velocity to reach it.
+         *
+         * @param target The target that the object was released in.
+         */
+        fun onReleasedInTarget(target: MagneticTarget)
+    }
+
+    private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject)
+    private val objectLocationOnScreen = IntArray(2)
+
+    /**
+     * Targets that have been added to this object. These will all be considered when determining
+     * magnetic fields and fling trajectories.
+     */
+    private val associatedTargets = ArrayList<MagneticTarget>()
+
+    private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
+    private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+
+    /** Whether touch events are presently occurring within the magnetic field area of a target. */
+    val objectStuckToTarget: Boolean
+        get() = targetObjectIsStuckTo != null
+
+    /** The target the object is stuck to, or null if the object is not stuck to any target. */
+    private var targetObjectIsStuckTo: MagneticTarget? = null
+
+    /**
+     * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent]
+     * will always return false and no magnetic effects will occur.
+     */
+    lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+    /**
+     * Sets whether forcefully flinging the object vertically towards a target causes it to be
+     * attracted to the target and then released immediately, despite never being dragged within the
+     * magnetic field.
+     */
+    var flingToTargetEnabled = true
+
+    /**
+     * If fling to target is enabled, forcefully flinging the object towards a target will cause
+     * it to be attracted to the target and then released immediately, despite never being dragged
+     * within the magnetic field.
+     *
+     * This sets the width of the area considered 'near' enough a target to be considered a fling,
+     * in terms of percent of the target view's width. For example, setting this to 3f means that
+     * flings towards a 100px-wide target will be considered 'near' enough if they're towards the
+     * 300px-wide area around the target.
+     *
+     * Flings whose trajectory intersects the area will be attracted and released - even if the
+     * target view itself isn't intersected:
+     *
+     * |             |
+     * |           0 |
+     * |          /  |
+     * |         /   |
+     * |      X /    |
+     * |.....###.....|
+     *
+     *
+     * Flings towards the target whose trajectories do not intersect the area will be treated as
+     * normal flings and the magnet will leave the object alone:
+     *
+     * |             |
+     * |             |
+     * |   0         |
+     * |  /          |
+     * | /    X      |
+     * |.....###.....|
+     *
+     */
+    var flingToTargetWidthPercent = 3f
+
+    /**
+     * Sets the minimum velocity (in pixels per second) required to fling an object to the target
+     * without dragging it into the magnetic field.
+     */
+    var flingToTargetMinVelocity = 4000f
+
+    /**
+     * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck
+     * to the target. If this velocity is reached, the object will be freed even if it wasn't moved
+     * outside the magnetic field radius.
+     */
+    var flingUnstuckFromTargetMinVelocity = 1000f
+
+    /**
+     * Sets the maximum velocity above which the object will not stick to the target. Even if the
+     * object is dragged through the magnetic field, it will not stick to the target until the
+     * velocity is below this value.
+     */
+    var stickToTargetMaxVelocity = 2000f
+
+    /**
+     * Enable or disable haptic vibration effects when the object interacts with the magnetic field.
+     *
+     * If you're experiencing crashes when the object enters targets, ensure that you have the
+     * android.permission.VIBRATE permission!
+     */
+    var hapticsEnabled = true
+
+    /** Whether the HAPTIC_FEEDBACK_ENABLED setting is true. */
+    private var systemHapticsEnabled = false
+
+    /** Default spring configuration to use for animating the object into a target. */
+    var springConfig = PhysicsAnimator.SpringConfig(
+            SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+
+    /**
+     * Spring configuration to use to spring the object into a target specifically when it's flung
+     * towards (rather than dragged near) it.
+     */
+    var flungIntoTargetSpringConfig = springConfig
+
+    init {
+        val hapticSettingObserver =
+                object : ContentObserver(Handler.getMain()) {
+            override fun onChange(selfChange: Boolean) {
+                systemHapticsEnabled =
+                        Settings.System.getIntForUser(
+                                context.contentResolver,
+                                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                                0,
+                                UserHandle.USER_CURRENT) != 0
+            }
+        }
+
+        context.contentResolver.registerContentObserver(
+                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED),
+                true /* notifyForDescendants */, hapticSettingObserver)
+
+        // Trigger the observer once to initialize systemHapticsEnabled.
+        hapticSettingObserver.onChange(false /* selfChange */)
+    }
+
+    /**
+     * Adds the provided MagneticTarget to this object. The object will now be attracted to the
+     * target if it strays within its magnetic field or is flung towards it.
+     *
+     * If this target (or its magnetic field) overlaps another target added to this object, the
+     * prior target will take priority.
+     */
+    fun addTarget(target: MagneticTarget) {
+        associatedTargets.add(target)
+        target.updateLocationOnScreen()
+    }
+
+    /**
+     * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target.
+     *
+     * @return The MagneticTarget instance for the given View. This can be used to change the
+     * target's magnetic field radius after it's been added. It can also be added to other
+     * magnetized objects.
+     */
+    fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget {
+        return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) }
+    }
+
+    /**
+     * Removes the given target from this object. The target will no longer attract the object.
+     */
+    fun removeTarget(target: MagneticTarget) {
+        associatedTargets.remove(target)
+    }
+
+    /**
+     * Provide this method with all motion events that move the magnetized object. If the
+     * location of the motion events moves within the magnetic field of a target, or indicate a
+     * fling-to-target gesture, this method will return true and you should not move the object
+     * yourself until it returns false again.
+     *
+     * Note that even when this method returns true, you should continue to pass along new motion
+     * events so that we know when the events move back outside the magnetic field area.
+     *
+     * This method will always return false if you haven't set a [magnetListener].
+     */
+    fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean {
+        // Short-circuit if we don't have a listener or any targets, since those are required.
+        if (associatedTargets.size == 0) {
+            return false
+        }
+
+        // When a gesture begins, recalculate target views' positions on the screen in case they
+        // have changed. Also, clear state.
+        if (ev.action == MotionEvent.ACTION_DOWN) {
+            updateTargetViewLocations()
+
+            // Clear the velocity tracker and assume we're not stuck to a target yet.
+            velocityTracker.clear()
+            targetObjectIsStuckTo = null
+        }
+
+        addMovement(ev)
+
+        val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
+            val distanceFromTargetCenter = hypot(
+                    ev.rawX - target.centerOnScreen.x,
+                    ev.rawY - target.centerOnScreen.y)
+            distanceFromTargetCenter < target.magneticFieldRadiusPx
+        }
+
+        // If we aren't currently stuck to a target, and we're in the magnetic field of a target,
+        // we're newly stuck.
+        val objectNewlyStuckToTarget =
+                !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null
+
+        // If we are currently stuck to a target, we're in the magnetic field of a target, and that
+        // target isn't the one we're currently stuck to, then touch events have moved into a
+        // adjacent target's magnetic field.
+        val objectMovedIntoDifferentTarget =
+                objectStuckToTarget &&
+                        targetObjectIsInMagneticFieldOf != null &&
+                        targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf
+
+        if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) {
+            velocityTracker.computeCurrentVelocity(1000)
+            val velX = velocityTracker.xVelocity
+            val velY = velocityTracker.yVelocity
+
+            // If the object is moving too quickly within the magnetic field, do not stick it. This
+            // only applies to objects newly stuck to a target. If the object is moved into a new
+            // target, it wasn't moving at all (since it was stuck to the previous one).
+            if (objectNewlyStuckToTarget && hypot(velX, velY) > stickToTargetMaxVelocity) {
+                return false
+            }
+
+            // This touch event is newly within the magnetic field - let the listener know, and
+            // animate sticking to the magnet.
+            targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
+            cancelAnimations()
+            magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
+            animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false)
+
+            vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+        } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
+            velocityTracker.computeCurrentVelocity(1000)
+
+            // This touch event is newly outside the magnetic field - let the listener know. It will
+            // move the object out of the target using its own movement logic.
+            cancelAnimations()
+            magnetListener.onUnstuckFromTarget(
+                    targetObjectIsStuckTo!!, velocityTracker.xVelocity, velocityTracker.yVelocity,
+                    wasFlungOut = false)
+            targetObjectIsStuckTo = null
+
+            vibrateIfEnabled(VibrationEffect.EFFECT_TICK)
+        }
+
+        // First, check for relevant gestures concluding with an ACTION_UP.
+        if (ev.action == MotionEvent.ACTION_UP) {
+
+            velocityTracker.computeCurrentVelocity(1000 /* units */)
+            val velX = velocityTracker.xVelocity
+            val velY = velocityTracker.yVelocity
+
+            // Cancel the magnetic animation since we might still be springing into the magnetic
+            // target, but we're about to fling away or release.
+            cancelAnimations()
+
+            if (objectStuckToTarget) {
+                if (hypot(velX, velY) > flingUnstuckFromTargetMinVelocity) {
+                    // If the object is stuck, but it was forcefully flung away from the target,
+                    // tell the listener so the object can be animated out of the target.
+                    magnetListener.onUnstuckFromTarget(
+                            targetObjectIsStuckTo!!, velX, velY, wasFlungOut = true)
+                } else {
+                    // If the object is stuck and not flung away, it was released inside the target.
+                    magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!)
+                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+                }
+
+                // Either way, we're no longer stuck.
+                targetObjectIsStuckTo = null
+                return true
+            }
+
+            // The target we're flinging towards, or null if we're not flinging towards any target.
+            val flungToTarget = associatedTargets.firstOrNull { target ->
+                isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY)
+            }
+
+            if (flungToTarget != null) {
+                // If this is a fling-to-target, animate the object to the magnet and then release
+                // it.
+                magnetListener.onStuckToTarget(flungToTarget)
+                targetObjectIsStuckTo = flungToTarget
+
+                animateStuckToTarget(flungToTarget, velX, velY, true) {
+                    targetObjectIsStuckTo = null
+                    magnetListener.onReleasedInTarget(flungToTarget)
+                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+                }
+
+                return true
+            }
+
+            // If it's not either of those things, we are not interested.
+            return false
+        }
+
+        return objectStuckToTarget // Always consume touch events if the object is stuck.
+    }
+
+    /** Plays the given vibration effect if haptics are enabled. */
+    @SuppressLint("MissingPermission")
+    private fun vibrateIfEnabled(effect: Int) {
+        if (hapticsEnabled && systemHapticsEnabled) {
+            vibrator.vibrate(effect.toLong())
+        }
+    }
+
+    /** Adds the movement to the velocity tracker using raw coordinates. */
+    private fun addMovement(event: MotionEvent) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        val deltaX = event.rawX - event.x
+        val deltaY = event.rawY - event.y
+        event.offsetLocation(deltaX, deltaY)
+        velocityTracker.addMovement(event)
+        event.offsetLocation(-deltaX, -deltaY)
+    }
+
+    /** Animates sticking the object to the provided target with the given start velocities.  */
+    private fun animateStuckToTarget(
+        target: MagneticTarget,
+        velX: Float,
+        velY: Float,
+        flung: Boolean,
+        after: (() -> Unit)? = null
+    ) {
+        target.updateLocationOnScreen()
+        getLocationOnScreen(underlyingObject, objectLocationOnScreen)
+
+        // Calculate the difference between the target's center coordinates and the object's.
+        // Animating the object's x/y properties by these values will center the object on top
+        // of the magnetic target.
+        val xDiff = target.centerOnScreen.x -
+                getWidth(underlyingObject) / 2f - objectLocationOnScreen[0]
+        val yDiff = target.centerOnScreen.y -
+                getHeight(underlyingObject) / 2f - objectLocationOnScreen[1]
+
+        val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig
+
+        cancelAnimations()
+
+        // Animate to the center of the target.
+        animator
+                .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX,
+                        springConfig)
+                .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
+                        springConfig)
+
+        if (after != null) {
+            animator.withEndActions(after)
+        }
+
+        animator.start()
+    }
+
+    /**
+     * Whether or not the provided values match a 'fast fling' towards the provided target. If it
+     * does, we consider it a fling-to-target gesture.
+     */
+    private fun isForcefulFlingTowardsTarget(
+        target: MagneticTarget,
+        rawX: Float,
+        rawY: Float,
+        velX: Float,
+        velY: Float
+    ): Boolean {
+        if (!flingToTargetEnabled) {
+            return false
+        }
+
+        // Whether velocity is sufficient, depending on whether we're flinging into a target at the
+        // top or the bottom of the screen.
+        val velocitySufficient =
+                if (rawY < target.centerOnScreen.y) velY > flingToTargetMinVelocity
+                else velY < flingToTargetMinVelocity
+
+        if (!velocitySufficient) {
+            return false
+        }
+
+        // Whether the trajectory of the fling intersects the target area.
+        var targetCenterXIntercept = rawX
+
+        // Only do math if the X velocity is non-zero, otherwise X won't change.
+        if (velX != 0f) {
+            // Rise over run...
+            val slope = velY / velX
+            // ...y = mx + b, b = y / mx...
+            val yIntercept = rawY - slope * rawX
+
+            // ...calculate the x value when y = the target's y-coordinate.
+            targetCenterXIntercept = (target.centerOnScreen.y - yIntercept) / slope
+        }
+
+        // The width of the area we're looking for a fling towards.
+        val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent
+
+        // Velocity was sufficient, so return true if the intercept is within the target area.
+        return targetCenterXIntercept > target.centerOnScreen.x - targetAreaWidth / 2 &&
+                targetCenterXIntercept < target.centerOnScreen.x + targetAreaWidth / 2
+    }
+
+    /** Cancel animations on this object's x/y properties. */
+    internal fun cancelAnimations() {
+        animator.cancel(xProperty, yProperty)
+    }
+
+    /** Updates the locations on screen of all of the [associatedTargets]. */
+    internal fun updateTargetViewLocations() {
+        associatedTargets.forEach { it.updateLocationOnScreen() }
+    }
+
+    /**
+     * Represents a target view with a magnetic field radius and cached center-on-screen
+     * coordinates.
+     *
+     * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then
+     * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to
+     * multiple objects.
+     */
+    class MagneticTarget(
+        internal val targetView: View,
+        var magneticFieldRadiusPx: Int
+    ) {
+        internal val centerOnScreen = PointF()
+
+        private val tempLoc = IntArray(2)
+
+        fun updateLocationOnScreen() {
+            targetView.getLocationOnScreen(tempLoc)
+
+            // Add half of the target size to get the center, and subtract translation since the
+            // target could be animating in while we're doing this calculation.
+            centerOnScreen.set(
+                    tempLoc[0] + targetView.width / 2f - targetView.translationX,
+                    tempLoc[1] + targetView.height / 2f - targetView.translationY)
+        }
+    }
+
+    companion object {
+
+        /**
+         * Magnetizes the given view. Magnetized views are attracted to one or more magnetic
+         * targets. Magnetic targets attract objects that are dragged near them, and hold them there
+         * unless they're moved away or released. Releasing objects inside a magnetic target
+         * typically performs an action on the object.
+         *
+         * Magnetized views can also be flung to targets, which will result in the view being pulled
+         * into the target and released as if it was dragged into it.
+         *
+         * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to
+         * receive event callbacks. In your touch handler, pass all MotionEvents that move this view
+         * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by
+         * MagnetizedObject and don't move the view unless it begins returning false again.
+         *
+         * The view will be moved via translationX/Y properties, and its
+         * width/height will be determined via getWidth()/getHeight(). If you are animating
+         * something other than a view, or want to position your view using properties other than
+         * translationX/Y, implement an instance of [MagnetizedObject].
+         *
+         * Note that the magnetic library can't re-order your view automatically. If the view
+         * renders on top of the target views, it will obscure the target when it sticks to it.
+         * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget].
+         */
+        @JvmStatic
+        fun <T : View> magnetizeView(view: T): MagnetizedObject<T> {
+            return object : MagnetizedObject<T>(
+                    view.context,
+                    view,
+                    DynamicAnimation.TRANSLATION_X,
+                    DynamicAnimation.TRANSLATION_Y) {
+                override fun getWidth(underlyingObject: T): Float {
+                    return underlyingObject.width.toFloat()
+                }
+
+                override fun getHeight(underlyingObject: T): Float {
+                    return underlyingObject.height.toFloat() }
+
+                override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) {
+                    underlyingObject.getLocationOnScreen(loc)
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index b6ca8d8e..7231b8a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -41,8 +41,8 @@
         InjectionInflationController inflationController = new InjectionInflationController(
                 SystemUIFactory.getInstance().getRootComponent());
         Context context = getContext();
-        KeyguardPresentation keyguardPresentation =
-                new KeyguardPresentation(context, context.getDisplay(), inflationController);
+        KeyguardPresentation keyguardPresentation = new KeyguardPresentation(context,
+                context.getDisplayNoVerify(), inflationController);
         keyguardPresentation.onCreate(null /*savedInstanceState */);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 3330d1e..6199181 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -93,6 +93,8 @@
                 mMockPluginManager, mMockColorExtractor, mMockContentResolver,
                 mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager);
 
+        mClockManager.addBuiltinClock(() -> new BubbleClockController(
+                getContext().getResources(), inflater, mMockColorExtractor));
         mClockManager.addOnClockChangedListener(mMockListener1);
         mClockManager.addOnClockChangedListener(mMockListener2);
         reset(mMockListener1, mMockListener2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
new file mode 100644
index 0000000..f1672b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2020 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.util.magnetictarget
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.MotionEvent
+import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MagnetizedObjectTest : SysuiTestCase() {
+    /** Incrementing value for fake MotionEvent timestamps. */
+    private var time = 0L
+
+    /** Value to add to each new MotionEvent's timestamp. */
+    private var timeStep = 100
+
+    private val underlyingObject = this
+
+    private lateinit var targetView: View
+
+    private val targetSize = 200
+    private val targetCenterX = 500
+    private val targetCenterY = 900
+    private val magneticFieldRadius = 200
+
+    private var objectX = 0f
+    private var objectY = 0f
+    private val objectSize = 50f
+
+    private lateinit var magneticTarget: MagnetizedObject.MagneticTarget
+    private lateinit var magnetizedObject: MagnetizedObject<*>
+    private lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+    private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+            objectX = value
+        }
+        override fun getValue(target: MagnetizedObjectTest?): Float {
+            return objectX
+        }
+    }
+
+    private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+            objectY = value
+        }
+
+        override fun getValue(target: MagnetizedObjectTest?): Float {
+            return objectY
+        }
+    }
+
+    @Before
+    fun setup() {
+        PhysicsAnimatorTestUtils.prepareForTest()
+
+        // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached
+        // to a real window (it'll always return x = 0, y = 0).
+        targetView = mock(View::class.java)
+        `when`(targetView.context).thenReturn(context)
+
+        // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's
+        // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900).
+        `when`(targetView.width).thenReturn(targetSize)  // width = 200
+        `when`(targetView.height).thenReturn(targetSize) // height = 200
+        doAnswer { invocation ->
+            (invocation.arguments[0] as IntArray).also { location ->
+                // Return the top left of the target.
+                location[0] = targetCenterX - targetSize / 2 // x = 400
+                location[1] = targetCenterY - targetSize / 2 // y = 800
+            }
+        }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
+        `when`(targetView.context).thenReturn(context)
+
+        magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
+
+        magnetListener = mock(MagnetizedObject.MagnetListener::class.java)
+        magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>(
+                context, underlyingObject, xProperty, yProperty) {
+            override fun getWidth(underlyingObject: MagnetizedObjectTest): Float {
+                return objectSize
+            }
+
+            override fun getHeight(underlyingObject: MagnetizedObjectTest): Float {
+                return objectSize
+            }
+
+            override fun getLocationOnScreen(
+                underlyingObject: MagnetizedObjectTest,
+                loc: IntArray
+            ) {
+                loc[0] = objectX.toInt()
+                loc[1] = objectY.toInt() }
+        }
+
+        magnetizedObject.magnetListener = magnetListener
+        magnetizedObject.addTarget(magneticTarget)
+
+        timeStep = 100
+    }
+
+    @Test
+    fun testMotionEventConsumption() {
+        // Start at (0, 0). No magnetic field here.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0, action = MotionEvent.ACTION_DOWN)))
+
+        // Move to (400, 400), which is solidly outside the magnetic field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 200, y = 200)))
+
+        // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were
+        // square. It's not, because they're not.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX - magneticFieldRadius + 5,
+                y = targetCenterY - magneticFieldRadius + 5)))
+
+        // Move to (400, 800). That's solidly in the radius so the magnetic target should begin
+        // consuming events.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX - 100,
+                y = targetCenterY - 100)))
+
+        // Release at (400, 800). Since we're in the magnetic target, it should return true and
+        // consume the ACTION_UP.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 400, y = 800, action = MotionEvent.ACTION_UP)))
+
+        // ACTION_DOWN outside the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 200, y = 200, action = MotionEvent.ACTION_DOWN)))
+
+        // Move to the center. We absolutely should consume events there.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY)))
+
+        // Drag out to (0, 0) and we should be returning false again.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0)))
+
+        // The ACTION_UP event shouldn't be consumed either since it's outside the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0, action = MotionEvent.ACTION_UP)))
+    }
+
+    @Test
+    fun testMotionEventConsumption_downInMagneticField() {
+        // We should consume DOWN events if they occur in the field.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
+    }
+
+    @Test
+    fun testMoveIntoAroundAndOutOfMagneticField() {
+        // Move around but don't touch the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 100),
+                getMotionEvent(x = 200, y = 200))
+
+        // You can't become unstuck if you were never stuck in the first place.
+        verify(magnetListener, never()).onStuckToTarget(magneticTarget)
+        verify(magnetListener, never()).onUnstuckFromTarget(
+                eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+
+        // Move into and then around inside the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+                getMotionEvent(x = targetCenterX, y = targetCenterY),
+                getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100))
+
+        // We should only have received one call to onStuckToTarget and none to unstuck.
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+        verify(magnetListener, never()).onUnstuckFromTarget(
+                eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+
+        // Move out of the field and then release.
+        dispatchMotionEvents(
+                getMotionEvent(x = 100, y = 100),
+                getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP))
+
+        // We should have received one unstuck call and no more stuck calls. We also should never
+        // have received an onReleasedInTarget call.
+        verify(magnetListener, times(1)).onUnstuckFromTarget(
+                eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMoveIntoOutOfAndBackIntoMagneticField() {
+        // Move into the field
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX - magneticFieldRadius,
+                        y = targetCenterY - magneticFieldRadius,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX, y = targetCenterY))
+
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+        verify(magnetListener, never()).onReleasedInTarget(magneticTarget)
+
+        // Move back out.
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX - magneticFieldRadius,
+                        y = targetCenterY - magneticFieldRadius))
+
+        verify(magnetListener, times(1)).onUnstuckFromTarget(
+                eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+        verify(magnetListener, never()).onReleasedInTarget(magneticTarget)
+
+        // Move in again and release in the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+                getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50),
+                getMotionEvent(x = targetCenterX, y = targetCenterY),
+                getMotionEvent(
+                        x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP))
+
+        verify(magnetListener, times(2)).onStuckToTarget(magneticTarget)
+        verify(magnetListener).onReleasedInTarget(magneticTarget)
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_towardsTarget() {
+        timeStep = 10
+
+        // Forcefully fling the object towards the target (but never touch the magnetic field).
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        // Nevertheless it should have ended up stuck to the target.
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_towardsButTooSlow() {
+        // Very, very slowly fling the object towards the target (but never touch the magnetic
+        // field). This value is only used to create MotionEvent timestamps, it will not block the
+        // test for 10 seconds.
+        timeStep = 10000
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        // No sticking should have occurred.
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_missTarget() {
+        timeStep = 10
+        // Forcefully fling the object down, but not towards the target.
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = 0,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = 0,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = 0,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMagnetAnimation() {
+        // Make sure the object starts at (0, 0).
+        assertEquals(0f, objectX)
+        assertEquals(0f, objectY)
+
+        // Trigger the magnet animation, and block the test until it ends.
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
+        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY,
+                action = MotionEvent.ACTION_DOWN))
+
+        // The object's (top-left) position should now position it centered over the target.
+        assertEquals(targetCenterX - objectSize / 2, objectX)
+        assertEquals(targetCenterY - objectSize / 2, objectY)
+    }
+
+    @Test
+    fun testMultipleTargets() {
+        val secondMagneticTarget = getSecondMagneticTarget()
+
+        // Drag into the second target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 900))
+
+        // Verify that we received an onStuck for the second target, and no others.
+        verify(magnetListener).onStuckToTarget(secondMagneticTarget)
+        verifyNoMoreInteractions(magnetListener)
+
+        // Drag into the original target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0),
+                getMotionEvent(x = 500, y = 900))
+
+        // We should have unstuck from the second one and stuck into the original one.
+        verify(magnetListener).onUnstuckFromTarget(
+                eq(secondMagneticTarget), anyFloat(), anyFloat(), eq(false))
+        verify(magnetListener).onStuckToTarget(magneticTarget)
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMultipleTargets_flingIntoSecond() {
+        val secondMagneticTarget = getSecondMagneticTarget()
+
+        timeStep = 10
+
+        // Fling towards the second target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 350),
+                getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP))
+
+        // Verify that we received an onStuck for the second target.
+        verify(magnetListener).onStuckToTarget(secondMagneticTarget)
+
+        // Fling towards the first target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 400, y = 350),
+                getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP))
+
+        // Verify that we received onStuck for the original target.
+        verify(magnetListener).onStuckToTarget(magneticTarget)
+    }
+
+    private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget {
+        // The first target view is at bounds (400, 800, 600, 1000) and it has a center of
+        // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900).
+        val secondTargetView = mock(View::class.java)
+        var secondTargetCenterX = 100
+        var secondTargetCenterY = 900
+
+        `when`(secondTargetView.context).thenReturn(context)
+        `when`(secondTargetView.width).thenReturn(targetSize)  // width = 200
+        `when`(secondTargetView.height).thenReturn(targetSize) // height = 200
+        doAnswer { invocation ->
+            (invocation.arguments[0] as IntArray).also { location ->
+                // Return the top left of the target.
+                location[0] = secondTargetCenterX - targetSize / 2 // x = 0
+                location[1] = secondTargetCenterY - targetSize / 2 // y = 800
+            }
+        }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any())
+
+        return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius)
+    }
+
+    /**
+     * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default).
+     * The event's time fields will be incremented by 10ms each time this is called, so tha
+     * VelocityTracker works.
+     */
+    private fun getMotionEvent(
+        x: Int,
+        y: Int,
+        action: Int = MotionEvent.ACTION_MOVE
+    ): MotionEvent {
+        return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0)
+                .also { time += timeStep }
+    }
+
+    /** Dispatch all of the provided events to the target view. */
+    private fun dispatchMotionEvents(vararg events: MotionEvent) {
+        events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) }
+    }
+
+    /** Prevents Kotlin from being mad that eq() is nullable. */
+    private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+}
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index df87ac9..a18f5da 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -33,6 +33,9 @@
  */
 @SystemApi(client = MODULE_LIBRARIES)
 public class TetheringConstants {
+    /** An explicit private class to avoid exposing constructor.*/
+    private TetheringConstants() { }
+
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering to
      * enable if any.
diff --git a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
index fcb113e..210fdc6 100644
--- a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
+++ b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
@@ -163,7 +163,7 @@
 
     private static boolean isHighResolution(Context context) {
         DisplayMetrics metrics = new DisplayMetrics();
-        context.getDisplay().getMetrics(metrics);
+        context.getDisplayNoVerify().getMetrics(metrics);
         return metrics.heightPixels > 2048 ||  metrics.widthPixels > 2048;
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7151d2b..a8a2791 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -281,7 +281,7 @@
     }
 
     private void computeMaximumWidgetBitmapMemory() {
-        Display display = mContext.getDisplay();
+        Display display = mContext.getDisplayNoVerify();
         Point size = new Point();
         display.getRealSize(size);
         // Cap memory usage at 1.5 times the size of the display
diff --git a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
index 813fc8d..14bd7d7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
+++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
@@ -75,7 +75,7 @@
         final TypedValue typedValue = new TypedValue();
         final Point point = new Point();
         final Context context = getContext();
-        context.getDisplay().getSize(point);
+        context.getDisplayNoVerify().getSize(point);
         context.getTheme().resolveAttribute(R.attr.autofillSaveCustomSubtitleMaxHeight,
                 typedValue, true);
         final View child = getChildAt(0);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5dc43ef..344b92f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -165,7 +165,7 @@
         // In full screen we only initialize size once assuming screen size never changes
         if (mFullScreen) {
             final Point outPoint = mTempPoint;
-            mContext.getDisplay().getSize(outPoint);
+            mContext.getDisplayNoVerify().getSize(outPoint);
             // full with of screen and half height of screen
             mContentWidth = LayoutParams.MATCH_PARENT;
             mContentHeight = outPoint.y / 2;
@@ -559,7 +559,7 @@
     }
 
     private static void resolveMaxWindowSize(Context context, Point outPoint) {
-        context.getDisplay().getSize(outPoint);
+        context.getDisplayNoVerify().getSize(outPoint);
         final TypedValue typedValue = sTempTypedValue;
         context.getTheme().resolveAttribute(R.attr.autofillDatasetPickerMaxWidth,
                 typedValue, true);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 3c37f73..e434be6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -76,9 +76,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -802,6 +800,7 @@
             final int size = in.getDataSize();
 
             if (excludedKeysForPackage != null && excludedKeysForPackage.contains(key)) {
+                Slog.i(TAG, "Skipping blocked key " + key);
                 in.skipEntityData();
                 continue;
             }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5db5115..d515332 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -862,10 +862,6 @@
             }
         }
 
-        public void requestSetAllowed(boolean allowed) {
-            mProvider.requestSetAllowed(allowed);
-        }
-
         public void onUserStarted(int userId) {
             synchronized (mLock) {
                 // clear the user's enabled state in order to force a reevalution of whether the
@@ -2931,18 +2927,6 @@
     private class LocalService extends LocationManagerInternal {
 
         @Override
-        public void requestSetProviderAllowed(String provider, boolean allowed) {
-            Preconditions.checkArgument(provider != null, "invalid null provider");
-
-            synchronized (mLock) {
-                LocationProviderManager manager = getLocationProviderManager(provider);
-                if (manager != null) {
-                    manager.requestSetAllowed(allowed);
-                }
-            }
-        }
-
-        @Override
         public boolean isProviderEnabledForUser(@NonNull String provider, int userId) {
             synchronized (mLock) {
                 LocationProviderManager manager = getLocationProviderManager(provider);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0d8eff5..a529f24 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7271,7 +7271,7 @@
 
         // Wait for the provider to be published...
         final long timeout =
-                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
         boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 8bbeabf..7cb8458 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -73,7 +73,7 @@
     private static final int GLOBAL_ID = -1;
 
     // The tolerance within which we consider something approximately equals.
-    private static final float EPSILON = 0.01f;
+    private static final float FLOAT_TOLERANCE = 0.01f;
 
     private final Object mLock = new Object();
     private final Context mContext;
@@ -267,8 +267,8 @@
             // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
             // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
             // comparison.
-            if (refreshRate < (minRefreshRate - EPSILON)
-                    || refreshRate > (maxRefreshRate + EPSILON)) {
+            if (refreshRate < (minRefreshRate - FLOAT_TOLERANCE)
+                    || refreshRate > (maxRefreshRate + FLOAT_TOLERANCE)) {
                 if (DEBUG) {
                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
                             + ", outside refresh rate bounds"
@@ -487,12 +487,18 @@
         public RefreshRateRange() {}
 
         public RefreshRateRange(float min, float max) {
-            if (min < 0 || max < 0 || min > max) {
+            if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
                 Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
                         + min + " " + max);
                 this.min = this.max = 0;
                 return;
             }
+            if (min > max) {
+                // Min and max are within epsilon of each other, but in the wrong order.
+                float t = min;
+                min = max;
+                max = t;
+            }
             this.min = min;
             this.max = max;
         }
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 433ec43..e9d94a5 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -366,21 +366,6 @@
     protected abstract void onExtraCommand(int uid, int pid, String command, Bundle extras);
 
     /**
-     * Requests a provider to enable itself for the given user id.
-     */
-    public final void requestSetAllowed(boolean allowed) {
-        // all calls into the provider must be moved onto the provider thread to prevent deadlock
-        mExecutor.execute(
-                obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed)
-                        .recycleOnUse());
-    }
-
-    /**
-     * Always invoked on the provider executor.
-     */
-    protected abstract void onRequestSetAllowed(boolean allowed);
-
-    /**
      * Dumps debug or log information. May be invoked from any thread.
      */
     public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5f44e04..685fb9e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1229,11 +1229,6 @@
         }
     }
 
-    @Override
-    protected void onRequestSetAllowed(boolean allowed) {
-        // do nothing - the gnss provider is always allowed
-    }
-
     private void deleteAidingData(Bundle extras) {
         int flags;
 
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 96ffaa6..87208a7 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -205,14 +205,6 @@
     }
 
     @Override
-    public void onRequestSetAllowed(boolean allowed) {
-        mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-            service.requestSetAllowed(allowed);
-        });
-    }
-
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mServiceWatcher.dump(fd, pw, args);
     }
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index b45b660..5ec06ca 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -64,11 +64,6 @@
     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
 
     @Override
-    protected void onRequestSetAllowed(boolean allowed) {
-        setAllowed(allowed);
-    }
-
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("last mock location=" + mLocation);
     }
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index f43669e..0f358e9 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -224,15 +224,6 @@
         }
     }
 
-    @Override
-    protected void onRequestSetAllowed(boolean allowed) {
-        synchronized (mOwnerLock) {
-            if (mProvider != null) {
-                mProvider.onRequestSetAllowed(allowed);
-            }
-        }
-    }
-
     /**
      * Dumps the current provider implementation.
      */
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 54dffff..1ba38cc 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -79,10 +79,5 @@
     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
 
     @Override
-    protected void onRequestSetAllowed(boolean allowed) {
-        // do nothing - the passive provider is always allowed
-    }
-
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
 }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 47a26f5..e734528 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -41,6 +41,7 @@
 import android.app.AppOpsManager.HistoricalUidOps;
 import android.app.INotificationManager;
 import android.app.ProcessMemoryState;
+import android.app.RuntimeAppOpAccessMessage;
 import android.app.StatsManager;
 import android.app.StatsManager.PullAtomMetadata;
 import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -377,6 +378,8 @@
                     return pullFaceSettings(atomTag, data);
                 case FrameworkStatsLog.APP_OPS:
                     return pullAppOps(atomTag, data);
+                case FrameworkStatsLog.RUNTIME_APP_OP_ACCESS:
+                    return pullRuntimeAppOpAccessMessage(atomTag, data);
                 case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS:
                     return pullNotificationRemoteViews(atomTag, data);
                 case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
@@ -539,6 +542,7 @@
         registerAppsOnExternalStorageInfo();
         registerFaceSettings();
         registerAppOps();
+        registerRuntimeAppOpAccessMessage();
         registerNotificationRemoteViews();
         registerDangerousPermissionState();
         registerDangerousPermissionStateSampled();
@@ -2834,6 +2838,17 @@
 
     }
 
+    private void registerRuntimeAppOpAccessMessage() {
+        int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mStatsCallbackImpl
+        );
+
+    }
+
     int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
         final long token = Binder.clearCallingIdentity();
         try {
@@ -2894,6 +2909,41 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+            RuntimeAppOpAccessMessage message = appOps.collectRuntimeAppOpAccessMessage();
+            if (message == null) {
+                Slog.i(TAG, "No runtime appop access message collected");
+                return StatsManager.PULL_SUCCESS;
+            }
+
+            StatsEvent.Builder e = StatsEvent.newBuilder();
+            e.setAtomId(atomTag);
+            e.writeInt(message.getUid());
+            e.writeString(message.getPackageName());
+            e.writeString(message.getOp());
+            if (message.getFeatureId() == null) {
+                e.writeString("");
+            } else {
+                e.writeString(message.getFeatureId());
+            }
+            e.writeString(message.getMessage());
+            e.writeInt(message.getSamplingStrategy());
+
+            pulledData.add(e.build());
+        } catch (Throwable t) {
+            // TODO: catch exceptions at a more granular level
+            Slog.e(TAG, "Could not read runtime appop access message", t);
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData,
             List<ParcelFileDescriptor> statsFiles) throws IOException {
         InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4cc4851..04fae97 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1163,8 +1163,12 @@
         // An activity is considered to be in multi-window mode if its task isn't fullscreen.
         final boolean inMultiWindowMode = inMultiWindowMode();
         if (inMultiWindowMode != mLastReportedMultiWindowMode) {
-            mLastReportedMultiWindowMode = inMultiWindowMode;
-            scheduleMultiWindowModeChanged(getConfiguration());
+            if (!inMultiWindowMode && mLastReportedPictureInPictureMode) {
+                updatePictureInPictureMode(null, false);
+            } else {
+                mLastReportedMultiWindowMode = inMultiWindowMode;
+                scheduleMultiWindowModeChanged(getConfiguration());
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 688f474..2f1cc05 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -658,8 +658,8 @@
     }
 
     @Override
-    public void resolveOverrideConfiguration(Configuration newParentConfig) {
-        super.resolveOverrideConfiguration(newParentConfig);
+    public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+        super.resolveTileOverrideConfiguration(newParentConfig);
         if (mTile != null) {
             // If this is a virtual child of a tile, simulate the parent-child relationship
             mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
@@ -742,8 +742,8 @@
                 setBounds(newBounds);
             } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
                 // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
-                resize(new Rect(newBounds), null /* tempTaskBounds */,
-                        null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+                resize(new Rect(newBounds), null /* configBounds */,
+                        PRESERVE_WINDOWS, true /* deferResume */);
             }
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -952,8 +952,8 @@
             }
 
             if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
-                resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        false /* preserveWindows */, true /* deferResume */);
+                resize(mTmpRect2, null /*configBounds*/,
+                        false /*preserveWindows*/, true /*deferResume*/);
             }
         } finally {
             if (showRecents && !alreadyInSplitScreenMode && isOnHomeDisplay()
@@ -1118,20 +1118,15 @@
         return r.getTask().mTaskId != taskId && r.appToken != notTop && r.canBeTopRunning();
     }
 
-    ActivityRecord isInStackLocked(IBinder token) {
-        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return isInStackLocked(r);
-    }
-
     ActivityRecord isInStackLocked(ActivityRecord r) {
         if (r == null) {
             return null;
         }
-        final Task task = r.getTask();
-        final ActivityStack stack = r.getRootTask();
-        if (stack != null && task.mChildren.contains(r) && mChildren.contains(task)) {
-            if (stack != this) Slog.w(TAG,
-                    "Illegal state! task does not point to stack it is in.");
+        final Task task = r.getRootTask();
+        if (task != null && r.isDescendantOf(task)) {
+            if (task != this) Slog.w(TAG, "Illegal state! task does not point to stack it is in. "
+                    + "stack=" + this + " task=" + task + " r=" + r
+                    + " callers=" + Debug.getCallers(15, "\n"));
             return r;
         }
         return null;
@@ -1207,7 +1202,7 @@
         }
 
         getDisplay().positionStackAtBottom(this, reason);
-        if (task != null) {
+        if (task != null && task != this) {
             positionChildAtBottom(task);
         }
 
@@ -1251,12 +1246,12 @@
         mCurrentUser = userId;
 
         super.switchUser(userId);
-        forAllTasks((t) -> {
-            if (t.showToCurrentUser()) {
+        forAllLeafTasks((t) -> {
+            if (t.showToCurrentUser() && t != this) {
                 mChildren.remove(t);
                 mChildren.add(t);
             }
-        }, true /* traverseTopToBottom */, this);
+        }, true /* traverseTopToBottom */);
     }
 
     void minimalResumeActivityLocked(ActivityRecord r) {
@@ -2450,16 +2445,16 @@
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
-        final boolean hasTask = hasChild(rTask);
+        final boolean isOrhasTask = rTask == this || hasChild(rTask);
         // mLaunchTaskBehind tasks get placed at the back of the task stack.
-        if (!r.mLaunchTaskBehind && allowMoveToFront && (!hasTask || newTask)) {
+        if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
             // Last activity in task had been removed or ActivityManagerService is reusing task.
             // Insert or replace.
             // Might not even be in.
             positionChildAtTop(rTask);
         }
         Task task = null;
-        if (!newTask && hasTask) {
+        if (!newTask && isOrhasTask) {
             final ActivityRecord occludingActivity = getActivity(
                     (ar) -> !ar.finishing && ar.occludesParent(), true, rTask);
             if (occludingActivity != null) {
@@ -2717,7 +2712,7 @@
     void finishVoiceTask(IVoiceInteractionSession session) {
         final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask,
                 PooledLambda.__(Task.class), session.asBinder());
-        forAllTasks(c, true /* traverseTopToBottom */, this);
+        forAllLeafTasks(c, true /* traverseTopToBottom */);
         c.recycle();
     }
 
@@ -2818,8 +2813,7 @@
             return false;
         }
         final Task task = srec.getTask();
-
-        if (!mChildren.contains(task) || !task.hasChild(srec)) {
+        if (!srec.isDescendantOf(this)) {
             return false;
         }
 
@@ -2932,20 +2926,20 @@
         getDisplay().mDisplayContent.prepareAppTransition(transit, false);
     }
 
-    final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+    final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
             AppTimeTracker timeTracker, String reason) {
-        moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+        moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
     }
 
-    final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+    final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
             AppTimeTracker timeTracker, boolean deferResume, String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
         final ActivityStack topStack = getDisplay().getTopStack();
-        final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null;
-        final int numTasks = getChildCount();
-        final int index = mChildren.indexOf(tr);
-        if (numTasks == 0 || index < 0)  {
+        final ActivityRecord topActivity = topStack != null
+                ? topStack.getTopNonFinishingActivity() : null;
+
+        if (tr != this && !tr.isDescendantOf(this)) {
             // nothing to do!
             if (noAnimation) {
                 ActivityOptions.abort(options);
@@ -3099,24 +3093,30 @@
 
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
     // Need to consolidate those calls points into this resize method so anyone can call directly.
-    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
-            boolean preserveWindows, boolean deferResume) {
-        if (!updateBoundsAllowed(bounds)) {
+    void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows,
+            boolean deferResume) {
+        if (!updateBoundsAllowed(displayedBounds)) {
             return;
         }
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
         mAtmService.deferWindowLayout();
         try {
+            // TODO: Why not just set this on the stack directly vs. on each tasks?
             // Update override configurations of all tasks in the stack.
-            final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
-                    taskBounds, tempTaskInsetBounds);
-            forAllTasks(c, true /* traverseTopToBottom */, this);
+                    displayedBounds, configBounds);
+            forAllTasks(c, true /* traverseTopToBottom */);
             c.recycle();
 
-            setBounds(bounds);
+            if (mBoundsAnimating) {
+                // Force to update task surface bounds and relayout windows, since configBounds
+                // remains unchanged during bounds animation.
+                updateSurfaceBounds();
+                getDisplay().setLayoutNeeded();
+                mWmService.requestTraversal();
+            }
 
             if (!deferResume) {
                 ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
@@ -3127,15 +3127,16 @@
         }
     }
 
-    private static void processTaskResizeBounds(Task task, Rect bounds, Rect insetBounds) {
+    private static void processTaskResizeBounds(
+            Task task, Rect displayedBounds, Rect configBounds) {
         if (!task.isResizeable()) return;
 
-        if (insetBounds != null && !insetBounds.isEmpty()) {
-            task.setOverrideDisplayedBounds(bounds);
-            task.setBounds(insetBounds);
+        if (configBounds != null && !configBounds.isEmpty()) {
+            task.setOverrideDisplayedBounds(displayedBounds);
+            task.setBounds(configBounds);
         } else {
             task.setOverrideDisplayedBounds(null);
-            task.setBounds(bounds);
+            task.setBounds(displayedBounds);
         }
     }
 
@@ -3150,7 +3151,7 @@
 
         final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
                 PooledLambda.__(Task.class), bounds);
-        forAllTasks(c, true /* traverseTopToBottom */, this);
+        forAllLeafTasks(c, true /* traverseTopToBottom */);
         c.recycle();
     }
 
@@ -3166,7 +3167,7 @@
 
         final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
                 PooledLambda.__(Task.class), bounds);
-        forAllTasks(c, true /* traverseTopToBottom */, this);
+        forAllLeafTasks(c, true /* traverseTopToBottom */);
         c.recycle();
     }
 
@@ -3262,7 +3263,7 @@
             return false;
         }
         final String prefix = "    ";
-        forAllTasks((task) -> {
+        forAllLeafTasks((task) -> {
             if (needSep) {
                 pw.println("");
             }
@@ -3280,7 +3281,7 @@
                     false /* traverseTopToBottom */);
             dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
                     dumpPackage, false, null, task);
-        }, true /* traverseTopToBottom */, this);
+        }, true /* traverseTopToBottom */);
         return true;
     }
 
@@ -3331,19 +3332,33 @@
         }
     }
 
-    Task createTask(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
-        return createTask(taskId, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
+    Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
+        return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
                 toTop, null /*activity*/, null /*source*/, null /*options*/);
     }
+    // TODO: Can be removed once we change callpoints creating stacks to be creating tasks.
+    /** Either returns this current task to be re-used or creates a new child task. */
+    Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
+            IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
+            ActivityRecord source, ActivityOptions options) {
 
-    Task createTask(int taskId, ActivityInfo info, Intent intent,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            boolean toTop, ActivityRecord activity, ActivityRecord source,
-            ActivityOptions options) {
-        final Task task = Task.create(
-                mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this);
-        // add the task to stack first, mTaskPositioner might need the stack association
-        addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+        Task task;
+        if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) {
+            // This stack will only contain one task, so just return itself since all stacks ara now
+            // tasks and all tasks are now stacks.
+            task = reuseAsLeafTask(voiceSession, voiceInteractor, info, activity);
+        } else {
+            // Create child task since this stack can contain multiple tasks.
+            final int taskId = activity != null
+                    ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId)
+                    : mStackSupervisor.getNextTaskIdForUser();
+            task = Task.create(
+                    mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this);
+
+            // add the task to stack first, mTaskPositioner might need the stack association
+            addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+        }
+
         int displayId = getDisplayId();
         if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
         final boolean isLockscreenShown = mAtmService.mStackSupervisor.getKeyguardController()
@@ -3353,6 +3368,7 @@
                 && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
             task.setBounds(getRequestedOverrideBounds());
         }
+
         return task;
     }
 
@@ -3559,10 +3575,6 @@
                     "Can't exit pinned mode if it's not pinned already.");
         }
 
-        if (mChildren.size() != 1) {
-            throw new RuntimeException("There should be only one task in a pinned stack.");
-        }
-
         // give pinned stack a chance to save current bounds, this should happen before reparent.
         final ActivityRecord top = topRunningNonOverlayTaskActivity();
         if (top != null && top.isVisible()) {
@@ -3592,12 +3604,12 @@
         final PooledConsumer c = PooledLambda.obtainConsumer(
                 ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
                 PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
-        forAllTasks(c, true /* traverseTopToBottom */, this);
+        forAllLeafTasks(c, true /* traverseTopToBottom */);
         c.recycle();
     }
 
     void prepareFreezingTaskBounds() {
-        forAllTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */, this);
+        forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
     }
 
     /**
@@ -3629,7 +3641,7 @@
             final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds,
                     PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(),
                     insetBounds, alignBottom);
-            forAllTasks(c, true /* traverseTopToBottom */, this);
+            forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         }
 
@@ -3902,6 +3914,12 @@
             return;
         }
 
+        if (child == this) {
+            // TODO: Fix call-points
+            moveToFront("positionChildAtTop");
+            return;
+        }
+
         positionChildAt(POSITION_TOP, child, true /* includingParents */);
         child.updateTaskMovement(true);
 
@@ -4316,19 +4334,19 @@
      * to the list of to be drawn windows the service is waiting for.
      */
     void beginImeAdjustAnimation() {
-        forAllTasks((t) -> {
+        forAllLeafTasks((t) -> {
             if (t.hasContentToDisplay()) {
                 t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
                 t.setWaitingForDrawnIfResizingChanged();
             }
-        }, true /* traverseTopToBottom */, this);
+        }, true /* traverseTopToBottom */);
     }
 
     /** Resets the resizing state of all windows. */
     void endImeAdjustAnimation() {
-        forAllTasks((t) -> {
+        forAllLeafTasks((t) -> {
             t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
-        }, true /* traverseTopToBottom */, this);
+        }, true /* traverseTopToBottom */);
     }
 
     private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
@@ -4572,19 +4590,15 @@
         return task != null;
     }
 
-    public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
+    public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
         synchronized (mWmService.mGlobalLock) {
             if (mCancelCurrentBoundsAnimation) {
                 return false;
             }
+            mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
         }
 
-        try {
-            mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
-        } catch (RemoteException e) {
-            // I don't believe you.
-        }
         return true;
     }
 
@@ -4730,7 +4744,7 @@
     /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
     void onPipAnimationEndResize() {
         mBoundsAnimating = false;
-        forAllTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */, this);
+        forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */);
         mWmService.requestTraversal();
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 70cd01b..97b6388 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -419,14 +419,29 @@
             mTopTask = fromStack.getTopMostTask();
 
             final PooledConsumer c = PooledLambda.obtainConsumer(
-                    MoveTaskToFullscreenHelper::processTask, this, PooledLambda.__(Task.class));
-            fromStack.forAllTasks(c, false /* traverseTopToBottom */, fromStack);
+                    MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class));
+            fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */);
             c.recycle();
             mToDisplay = null;
             mTopTask = null;
         }
 
-        private void processTask(Task task) {
+        private void processLeafTask(Task task) {
+            // This is a one level task that we don't need to create stack for reparenting to.
+            if (task.isRootTask() && DisplayContent.alwaysCreateStack(WINDOWING_MODE_FULLSCREEN,
+                    task.getActivityType())) {
+                final ActivityStack stack = (ActivityStack) task;
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                if (mToDisplay.getDisplayId() != stack.getDisplayId()) {
+                    mToDisplay.moveStackToDisplay(stack, mOnTop);
+                } else if (mOnTop) {
+                    mToDisplay.positionStackAtTop(stack, false /* includingParents */);
+                } else {
+                    mToDisplay.positionStackAtBottom(stack);
+                }
+                return;
+            }
+
             final ActivityStack toStack = mToDisplay.getOrCreateStack(
                     null, mTmpOptions, task, task.getActivityType(), mOnTop);
 
@@ -1428,8 +1443,7 @@
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
             if (stack.shouldResizeStackWithLaunchBounds()) {
-                stack.resize(bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        !PRESERVE_WINDOWS, !DEFER_RESUME);
+                stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
             } else {
                 // WM resizeTask must be done after the task is moved to the correct stack,
                 // because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1443,7 +1457,7 @@
         }
 
         final ActivityRecord r = task.getTopNonFinishingActivity();
-        currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+        currentStack.moveTaskToFront(task, false /* noAnimation */, options,
                 r == null ? null : r.appTimeTracker, reason);
 
         if (DEBUG_STACK) Slog.d(TAG_STACK,
@@ -1589,7 +1603,7 @@
                 false /* deferResume */);
     }
 
-    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+    void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
             boolean preserveWindows, boolean deferResume) {
 
@@ -1607,7 +1621,7 @@
 
         if (mDockedStackResizing) {
             mHasPendingDockedBounds = true;
-            mPendingDockedBounds = copyOrNull(dockedBounds);
+            mPendingDockedBounds = copyOrNull(displayedBounds);
             mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
             mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
             mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
@@ -1620,13 +1634,13 @@
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
             mAllowDockedStackResize = false;
             ActivityRecord r = stack.topRunningActivity();
-            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+            stack.resize(displayedBounds, tempDockedTaskBounds,
                     !PRESERVE_WINDOWS, DEFER_RESUME);
 
             // TODO: Checking for isAttached might not be needed as if the user passes in null
             // dockedBounds then they want the docked stack to be dismissed.
             if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                    || (dockedBounds == null && !stack.isAttached())) {
+                    || (displayedBounds == null && !stack.isAttached())) {
                 // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
@@ -1654,7 +1668,7 @@
                         // interaction.
                         continue;
                     }
-                    current.getStackDockedModeBounds(dockedBounds,
+                    current.getStackDockedModeBounds(displayedBounds,
                             tempOtherTaskBounds /* currentTempTaskBounds */,
                             tempRect /* outStackBounds */,
                             otherTaskRect /* outTempTaskBounds */);
@@ -1669,9 +1683,7 @@
                                 + " non-fullscreen stack");
                     }
 
-                    current.resize(tempRect,
-                            !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
-                            tempOtherTaskInsetBounds, preserveWindows, deferResume);
+                    current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume);
                 }
             }
             if (!deferResume) {
@@ -1684,7 +1696,7 @@
         }
     }
 
-    void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+    void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) {
         // TODO(multi-display): The display containing the stack should be passed in.
         final ActivityStack stack =
                 mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
@@ -1696,23 +1708,22 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
         mService.deferWindowLayout();
         try {
-            Rect insetBounds = null;
-            if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
+            Rect configBounds = null;
+            if (inConfigBounds != null) {
                 // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
-                insetBounds = tempRect;
-                insetBounds.top = 0;
-                insetBounds.left = 0;
-                insetBounds.right = tempPinnedTaskBounds.width();
-                insetBounds.bottom = tempPinnedTaskBounds.height();
+                configBounds = tempRect;
+                configBounds.top = 0;
+                configBounds.left = 0;
+                configBounds.right = inConfigBounds.width();
+                configBounds.bottom = inConfigBounds.height();
             }
-            if (pinnedBounds != null && tempPinnedTaskBounds == null) {
+            if (displayedBounds != null && inConfigBounds == null) {
                 // We have finished the animation into PiP, and are resizing the tasks to match the
                 // stack bounds, while layouts are deferred, update any task state as a part of
                 // transitioning it from fullscreen into a floating state.
                 stack.onPipAnimationEndResize();
             }
-            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds, !PRESERVE_WINDOWS,
-                    !DEFER_RESUME);
+            stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
         } finally {
             mService.continueWindowLayout();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -1742,7 +1753,7 @@
         } else {
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
-            stack.forAllTasks(c, true /* traverseTopToBottom */, stack);
+            stack.forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         }
     }
@@ -2755,6 +2766,10 @@
                 deferUpdateRecentsHomeStackBounds();
                 // TODO(multi-display): currently recents animation only support default display.
                 mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+                // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+                // Unset launching windowing mode to prevent creating split-screen-primary stack
+                // in RWC#anyTaskForId() below.
+                activityOptions.setLaunchWindowingMode(WINDOWING_MODE_UNDEFINED);
             }
 
             task = mRootWindowContainer.anyTaskForId(taskId,
@@ -2764,6 +2779,9 @@
                 mWindowManager.executeAppTransition();
                 throw new IllegalArgumentException(
                         "startActivityFromRecents: Task " + taskId + " not found.");
+            } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                    && task.getWindowingMode() != windowingMode) {
+                mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
             }
 
             if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2a2ab4b..600a125 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2366,7 +2366,7 @@
                     // task on top there.
                     // Defer resuming the top activity while moving task to top, since the
                     // current task-top activity may not be the activity that should be resumed.
-                    mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
+                    mTargetStack.moveTaskToFront(intentTask, mNoAnimation, mOptions,
                             mStartActivity.appTimeTracker, DEFER_RESUME,
                             "bringingFoundTaskToFront");
                     mMovedToFront = !isSplitScreenTopStack;
@@ -2396,8 +2396,7 @@
 
     private void setNewTask(Task taskToAffiliate) {
         final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
-        final Task task = mTargetStack.createTask(
-                mSupervisor.getNextTaskIdForUser(mStartActivity.mUserId),
+        final Task task = mTargetStack.reuseOrCreateTask(
                 mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                 mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 882d5c7..344d4a5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2760,9 +2760,17 @@
             throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
                     + " non-standard task " + taskId + " to split-screen windowing mode");
         }
+        if (!task.supportsSplitScreenWindowingMode()) {
+            return false;
+        }
 
         final int prevMode = task.getWindowingMode();
-        final ActivityStack stack = task.getStack();
+        moveTaskToSplitScreenPrimaryTile(task, toTop);
+        return prevMode != task.getWindowingMode();
+    }
+
+    void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
+        ActivityStack stack = task.getStack();
         TaskTile tile = null;
         for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
             tile = stack.getDisplay().getStackAt(i).asTile();
@@ -2776,7 +2784,6 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
         mTaskOrganizerController.applyContainerTransaction(wct, null);
-        return prevMode != task.getWindowingMode();
     }
 
     /**
@@ -3247,8 +3254,9 @@
                 }
 
                 final ActivityStack stack = r.getRootTask();
-                final Task task = stack.createTask(
-                        mStackSupervisor.getNextTaskIdForUser(r.mUserId), ainfo, intent, !ON_TOP);
+                final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
+                        stack.getActivityType(), !ON_TOP, ainfo, intent);
+
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
                     stack.removeChild(task, "addAppTask");
@@ -4426,12 +4434,12 @@
     }
 
     @Override
-    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+    public void resizePinnedStack(Rect displayedBounds, Rect configBounds) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+                mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 9f54e49e0..b1d5359 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -52,7 +52,7 @@
      * animation is now invalid and not required. In such a case, the cancel will trigger the
      * animation end callback as well, but will not send any further size changes.
      */
-    boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds);
+    boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds);
 
     /** Sets the alpha of the animation target */
     boolean setPinnedStackAlpha(float alpha);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e60ab3f..0029dc8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4415,7 +4415,7 @@
         ArrayList<Task> getVisibleTasks() {
             final ArrayList<Task> visibleTasks = new ArrayList<>();
             forAllTasks(task -> {
-                if (!task.isRootTask() && task.isVisible()) {
+                if (task.isLeafTask() && task.isVisible()) {
                     visibleTasks.add(task);
                 }
             });
@@ -4537,6 +4537,8 @@
                         true /* includingParents */);
             }
 
+            child.updateTaskMovement(moveToTop);
+
             setLayoutNeeded();
         }
 
@@ -5790,7 +5792,7 @@
         return null;
     }
 
-    boolean alwaysCreateStack(int windowingMode, int activityType) {
+    static boolean alwaysCreateStack(int windowingMode, int activityType) {
         // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
         // modes so that we can manage visual ordering and return types correctly.
         return activityType == ACTIVITY_TYPE_STANDARD
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 64c5faa..57babb0 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -558,16 +558,14 @@
 
     @VisibleForTesting
     boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
-        final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
-        if (w == null) {
-            return false;
-        }
         // Display doesn't need to be frozen because application has been started in correct
         // rotation already, so the rest of the windows can use seamless rotation.
-        if (w.mToken.hasFixedRotationTransform()) {
+        if (mDisplayContent.mFixedRotationLaunchingApp != null) {
             return true;
         }
-        if (w != mDisplayContent.mCurrentFocus) {
+
+        final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
+        if (w == null || w != mDisplayContent.mCurrentFocus) {
             return false;
         }
         // We only enable seamless rotation if the top window has requested it and is in the
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 9d985d7..c92de2b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -76,7 +76,7 @@
         // to be visible (such as performing Recents animation).
         final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
                 && mContiner.isTopActivityFocusable()
-                && mContiner.isInStackLocked(starting) == null;
+                && (starting == null || !starting.isDescendantOf(mContiner));
 
         final PooledConsumer f = PooledLambda.obtainConsumer(
                 EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b0492be..e92bbaa 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -334,7 +334,7 @@
                         if (sendUserLeaveHint) {
                             // Setting this allows the previous app to PiP.
                             mStackSupervisor.mUserLeaving = true;
-                            targetStack.moveTaskToFrontLocked(targetActivity.getTask(),
+                            targetStack.moveTaskToFront(targetActivity.getTask(),
                                     true /* noAnimation */, null /* activityOptions */,
                                     targetActivity.appTimeTracker,
                                     "RecentsAnimation.onAnimationFinished()");
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e923e64..57c877f 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -376,7 +376,7 @@
             final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
 	            { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
                     visibleTasks);
-            targetStack.forAllTasks(c, true /* traverseTopToBottom */, targetStack);
+            targetStack.forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         }
 
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 63346b9..45f8a15 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -67,7 +67,7 @@
 
         final PooledConsumer c = PooledLambda.obtainConsumer(
                 ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class));
-        targetTask.mWmService.mRoot.forAllTasks(c, true /*traverseTopToBottom*/, mTargetStack);
+        targetTask.mWmService.mRoot.forAllLeafTasks(c, true /*traverseTopToBottom*/);
         c.recycle();
 
         processPendingReparentActivities();
@@ -245,9 +245,8 @@
                 if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
                         + r + " out to bottom task " + targetTask);
             } else {
-                targetTask = mTargetStack.createTask(
-                        atmService.mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info,
-                        null /* intent */, false /* toTop */);
+                targetTask = mTargetStack.reuseOrCreateTask(
+                        r.info, null /*intent*/, false /*toTop*/);
                 targetTask.affinityIntent = r.intent;
                 createdTasks.add(targetTask);
                 if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2596452..aa6bdfd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2137,10 +2137,7 @@
                         r.getActivityType(), ON_TOP, r.info, r.intent);
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
-
-                Task newTask = stack.createTask(mStackSupervisor.getNextTaskIdForUser(r.mUserId),
-                        r.info, r.intent, true);
-                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+                r.reparent(stack, MAX_VALUE, "moveActivityToStack");
             }
 
             stack.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -2407,7 +2404,7 @@
         final PooledConsumer c = PooledLambda.obtainConsumer(
                 RootWindowContainer::processTaskForStackInfo, PooledLambda.__(Task.class), info,
                 currentIndex);
-        stack.forAllTasks(c, false /* traverseTopToBottom */, stack);
+        stack.forAllLeafTasks(c, false /* traverseTopToBottom */);
         c.recycle();
 
         final ActivityRecord top = stack.topRunningActivity();
@@ -3302,7 +3299,7 @@
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     RootWindowContainer::taskTopActivityIsUser, this, PooledLambda.__(Task.class),
                     userId);
-            forAllTasks(c);
+            forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         } finally {
             mService.continueWindowLayout();
@@ -3321,14 +3318,6 @@
      * @return {@code true} if the top activity looks like it belongs to {@param userId}.
      */
     private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
-        // TODO(b/80414790): having utilities to loop for all leaf tasks from caller vs. checking
-        //  leaf tasks here.
-        if (!task.isLeafTask()) {
-            // No op if not a leaf task since we don't want to report root tasks to
-            // TaskStackListeners.
-            return;
-        }
-
         // To handle the case that work app is in the task but just is not the top one.
         final ActivityRecord activityRecord = task.getTopNonFinishingActivity();
         final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
@@ -3430,18 +3419,8 @@
     }
 
     ActivityRecord isInAnyStack(IBinder token) {
-        int numDisplays = getChildCount();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final DisplayContent display = getChildAt(displayNdx);
-            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getStackAt(stackNdx);
-                final ActivityRecord r = stack.isInStackLocked(token);
-                if (r != null) {
-                    return r;
-                }
-            }
-        }
-        return null;
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        return (r != null && r.isDescendantOf(this)) ? r : null;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 6ebbf77..9593ea0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -76,7 +76,7 @@
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
                 PooledLambda.__(Task.class));
-        root.forAllTasks(c, false);
+        root.forAllLeafTasks(c, false);
         c.recycle();
 
         // Take the first {@param maxNum} tasks and create running task infos for them
@@ -93,9 +93,6 @@
     }
 
     private void processTask(Task task) {
-        if (task.isRootTask()) {
-            return;
-        }
         if (task.getTopNonFinishingActivity() == null) {
             // Skip if there are no activities in the task
             return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5554b1d..f93e392 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -225,8 +225,8 @@
 
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
-    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
-    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
+    IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
+    IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
     Intent intent;          // The original intent that started the task. Note that this value can
                             // be null.
     Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
@@ -573,6 +573,15 @@
         mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
     }
 
+    Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
+            ActivityInfo info, ActivityRecord activity) {
+        voiceSession = _voiceSession;
+        voiceInteractor = _voiceInteractor;
+        setIntent(activity);
+        setMinDimensions(info);
+        return this;
+    }
+
     private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
         if (hasChild()) {
             return;
@@ -1006,7 +1015,7 @@
     }
 
     /** Sets the original minimal width and height. */
-    private void setMinDimensions(ActivityInfo info) {
+    void setMinDimensions(ActivityInfo info) {
         if (info != null && info.windowLayout != null) {
             mMinWidth = info.windowLayout.minWidth;
             mMinHeight = info.windowLayout.minHeight;
@@ -2178,16 +2187,20 @@
         return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
     }
 
+    void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+        super.resolveOverrideConfiguration(newParentConfig);
+    }
+
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        if (isRootTask()) {
-            super.resolveOverrideConfiguration(newParentConfig);
+        if (!isLeafTask()) {
+            resolveTileOverrideConfiguration(newParentConfig);
             return;
         }
         mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
-        super.resolveOverrideConfiguration(newParentConfig);
+        resolveTileOverrideConfiguration(newParentConfig);
         int windowingMode =
-                getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
             windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
         }
@@ -2394,7 +2407,7 @@
         final int[] currentCount = {0};
         final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
                 PooledLambda.__(Task.class), currentCount);
-        forAllTasks(c, false /* traverseTopToBottom */, this);
+        forAllLeafTasks(c, false /* traverseTopToBottom */);
         c.recycle();
         return currentCount[0];
     }
@@ -2616,6 +2629,7 @@
      */
     void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
         if (overrideDisplayedBounds != null) {
+            adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds);
             mOverrideDisplayedBounds.set(overrideDisplayedBounds);
         } else {
             mOverrideDisplayedBounds.setEmpty();
@@ -3076,16 +3090,33 @@
     }
 
     @Override
-    void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) {
-        super.forAllTasks(callback, traverseTopToBottom, excludedTask);
-        if (excludedTask != this) {
-            callback.accept(this);
+    void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
+        final int count = mChildren.size();
+        boolean isLeafTask = true;
+        if (traverseTopToBottom) {
+            for (int i = count - 1; i >= 0; --i) {
+                final Task child = mChildren.get(i).asTask();
+                if (child != null) {
+                    isLeafTask = false;
+                    child.forAllLeafTasks(callback, traverseTopToBottom);
+                }
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                final Task child = mChildren.get(i).asTask();
+                if (child != null) {
+                    isLeafTask = false;
+                    child.forAllLeafTasks(callback, traverseTopToBottom);
+                }
+            }
         }
+        if (isLeafTask) callback.accept(this);
     }
 
     @Override
     void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
-        forAllTasks(callback, traverseTopToBottom, null /* excludedTask */);
+        super.forAllTasks(callback, traverseTopToBottom);
+        callback.accept(this);
     }
 
     @Override
@@ -3267,7 +3298,7 @@
         pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
         pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
         pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
-        pw.print(" mCallingPackage="); pw.println(mCallingPackage);
+        pw.print(" mCallingPackage="); pw.print(mCallingPackage);
         pw.print(" mCallingFeatureId="); pw.println(mCallingFeatureId);
         if (affinity != null || rootAffinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.print(affinity);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 202e089..6caa27c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -547,8 +547,7 @@
             final ActivityStack stack = (ActivityStack) container;
             if (stack.inPinnedWindowingMode()) {
                 stack.resize(config.windowConfiguration.getBounds(),
-                        null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        PRESERVE_WINDOWS, true /* deferResume */);
+                        null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index da996dc..7a4d0b0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -615,7 +615,7 @@
     void positionChildAt(int position, E child, boolean includingParents) {
 
         if (child.getParent() != this) {
-            throw new IllegalArgumentException("removeChild: container=" + child.getName()
+            throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
                     + " is not a child of container=" + getName()
                     + " current parent=" + child.getParent());
         }
@@ -1459,15 +1459,15 @@
         }
     }
 
-    void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) {
+    void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
         final int count = mChildren.size();
         if (traverseTopToBottom) {
             for (int i = count - 1; i >= 0; --i) {
-                mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask);
+                mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom);
             }
         } else {
             for (int i = 0; i < count; i++) {
-                mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask);
+                mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom);
             }
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 012fdfc..b192dbd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -186,7 +186,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.location.LocationManager;
-import android.location.LocationManagerInternal;
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
@@ -2131,10 +2130,6 @@
             return mContext.getSystemService(LocationManager.class);
         }
 
-        LocationManagerInternal getLocationManagerInternal() {
-            return LocalServices.getService(LocationManagerInternal.class);
-        }
-
         IWindowManager getIWindowManager() {
             return IWindowManager.Stub
                     .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -11703,17 +11698,6 @@
     }
 
     @Override
-    public void requestSetLocationProviderAllowed(ComponentName who, String provider,
-            boolean providerAllowed) {
-        Objects.requireNonNull(who, "ComponentName is null");
-        enforceDeviceOwner(who);
-
-        mInjector.binderWithCleanCallingIdentity(
-                () -> mInjector.getLocationManagerInternal().requestSetProviderAllowed(provider,
-                        providerAllowed));
-    }
-
-    @Override
     public boolean setTime(ComponentName who, long millis) {
         Objects.requireNonNull(who, "ComponentName is null in setTime");
         enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 7eb2176..27c1692 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -51,7 +51,6 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.telecom.TelecomManager;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -275,40 +274,35 @@
                 mContext.getPackageName(), intentFilter, callingUserId);
     }
 
-    /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
-    public void reportAppTargetEvent(@NonNull AppTargetEvent event,
+    /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
+    public void reportShareTargetEvent(@NonNull AppTargetEvent event,
             @Nullable IntentFilter intentFilter) {
         AppTarget appTarget = event.getTarget();
-        ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null;
-        if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
+        if (appTarget == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
             return;
         }
-        PackageData packageData = getPackage(appTarget.getPackageName(),
-                appTarget.getUser().getIdentifier());
-        if (packageData == null) {
-            return;
-        }
+        UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
+        PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
+        String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+        @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
+        EventHistoryImpl eventHistory;
         if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
-            String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
-            String shortcutId = shortcutInfo.getId();
-            if (packageData.getConversationStore().getConversation(shortcutId) == null
-                    || TextUtils.isEmpty(mimeType)) {
+            // Direct share event
+            if (appTarget.getShortcutInfo() == null) {
                 return;
             }
-            EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
-                    EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
-            @Event.EventType int eventType;
-            if (mimeType.startsWith("text/")) {
-                eventType = Event.TYPE_SHARE_TEXT;
-            } else if (mimeType.startsWith("image/")) {
-                eventType = Event.TYPE_SHARE_IMAGE;
-            } else if (mimeType.startsWith("video/")) {
-                eventType = Event.TYPE_SHARE_VIDEO;
-            } else {
-                eventType = Event.TYPE_SHARE_OTHER;
+            String shortcutId = appTarget.getShortcutInfo().getId();
+            if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+                addOrUpdateConversationInfo(appTarget.getShortcutInfo());
             }
-            eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
+            eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                    EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
+        } else {
+            // App share event
+            eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                    EventStore.CATEGORY_CLASS_BASED, appTarget.getClassName());
         }
+        eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
     }
 
     /** Prunes the data for the specified user. */
@@ -334,6 +328,17 @@
         });
     }
 
+    private int mimeTypeToShareEventType(String mimeType) {
+        if (mimeType.startsWith("text/")) {
+            return Event.TYPE_SHARE_TEXT;
+        } else if (mimeType.startsWith("image/")) {
+            return Event.TYPE_SHARE_IMAGE;
+        } else if (mimeType.startsWith("video/")) {
+            return Event.TYPE_SHARE_VIDEO;
+        }
+        return Event.TYPE_SHARE_OTHER;
+    }
+
     private void pruneUninstalledPackageData(@NonNull UserData userData) {
         Set<String> installApps = new ArraySet<>();
         mPackageManagerInternal.forEachInstalledPackage(
@@ -409,12 +414,13 @@
                 EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
     }
 
+    private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) {
+        return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0;
+    }
+
     @VisibleForTesting
     @WorkerThread
-    void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) {
-        if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) {
-            return;
-        }
+    void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
         UserData userData = getUnlockedUserData(shortcutInfo.getUserId());
         if (userData == null) {
             return;
@@ -430,24 +436,24 @@
         builder.setShortcutId(shortcutInfo.getId());
         builder.setLocusId(shortcutInfo.getLocusId());
         builder.setShortcutFlags(shortcutInfo.getFlags());
+        builder.setContactUri(null);
+        builder.setContactPhoneNumber(null);
+        builder.setContactStarred(false);
 
-        Person person = shortcutInfo.getPersons()[0];
-        builder.setPersonImportant(person.isImportant());
-        builder.setPersonBot(person.isBot());
-        String contactUri = person.getUri();
-        if (contactUri != null) {
-            ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
-            if (helper.query(contactUri)) {
-                builder.setContactUri(helper.getContactUri());
-                builder.setContactStarred(helper.isStarred());
-                builder.setContactPhoneNumber(helper.getPhoneNumber());
+        if (shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0) {
+            Person person = shortcutInfo.getPersons()[0];
+            builder.setPersonImportant(person.isImportant());
+            builder.setPersonBot(person.isBot());
+            String contactUri = person.getUri();
+            if (contactUri != null) {
+                ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+                if (helper.query(contactUri)) {
+                    builder.setContactUri(helper.getContactUri());
+                    builder.setContactStarred(helper.isStarred());
+                    builder.setContactPhoneNumber(helper.getPhoneNumber());
+                }
             }
-        } else {
-            builder.setContactUri(null);
-            builder.setContactPhoneNumber(null);
-            builder.setContactStarred(false);
         }
-
         conversationStore.addOrUpdate(builder.build());
     }
 
@@ -625,7 +631,9 @@
                 List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
                         /*shortcutIds=*/ null);
                 for (ShortcutInfo shortcut : shortcuts) {
-                    onShortcutAddedOrUpdated(shortcut);
+                    if (isPersonShortcut(shortcut)) {
+                        addOrUpdateConversationInfo(shortcut);
+                    }
                 }
             });
         }
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 19cf8af..c89dadc 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -73,6 +73,7 @@
      */
     @MainThread
     public void onAppTargetEvent(AppTargetEvent event) {
+        mCallbackExecutor.execute(() -> reportAppTargetEvent(event));
     }
 
     /**
@@ -104,6 +105,11 @@
         return mUpdatePredictionsMethod;
     }
 
+    /** To be overridden by the subclass to report app target event. */
+    @WorkerThread
+    void reportAppTargetEvent(AppTargetEvent event) {
+    }
+
     /** To be overridden by the subclass to predict the targets. */
     @WorkerThread
     void predictTargets() {
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 90d8216..8e5d75b 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,7 +16,6 @@
 
 package com.android.server.people.prediction;
 
-import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -28,15 +27,18 @@
 import android.content.IntentFilter;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.util.Range;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
 import com.android.server.people.data.ConversationInfo;
 import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
 import com.android.server.people.data.EventHistory;
 import com.android.server.people.data.PackageData;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -52,89 +54,139 @@
                 ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
     }
 
-    @MainThread
-    @Override
-    public void onAppTargetEvent(AppTargetEvent event) {
-        getDataManager().reportAppTargetEvent(event, mIntentFilter);
-    }
-
+    /** Reports chosen history of direct/app share targets. */
     @WorkerThread
     @Override
-    protected void predictTargets() {
-        List<ShareTarget> shareTargets = getShareTargets();
-        // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
-        List<AppTarget> appTargets = new ArrayList<>();
-        for (ShareTarget shareTarget : shareTargets) {
-
-            ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
-            AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
-            String shareTargetClassName =
-                    shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
-            AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
-                    .setClassName(shareTargetClassName)
-                    .build();
-            appTargets.add(appTarget);
-            if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
-                break;
-            }
-        }
-        updatePredictions(appTargets);
+    void reportAppTargetEvent(AppTargetEvent event) {
+        getDataManager().reportShareTargetEvent(event, mIntentFilter);
     }
 
-    @VisibleForTesting
-    List<ShareTarget> getShareTargets() {
+    /** Provides prediction on direct share targets */
+    @WorkerThread
+    @Override
+    void predictTargets() {
+        List<ShareTarget> shareTargets = getDirectShareTargets();
+        rankTargets(shareTargets);
+        List<AppTarget> res = new ArrayList<>();
+        for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
+                shareTargets.size()); i++) {
+            res.add(shareTargets.get(i).getAppTarget());
+        }
+        updatePredictions(res);
+    }
+
+    /** Provides prediction on app share targets */
+    @WorkerThread
+    @Override
+    void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+        List<ShareTarget> shareTargets = getAppShareTargets(targets);
+        rankTargets(shareTargets);
+        List<AppTarget> appTargetList = new ArrayList<>();
+        shareTargets.forEach(t -> appTargetList.add(t.getAppTarget()));
+        callback.accept(appTargetList);
+    }
+
+    private void rankTargets(List<ShareTarget> shareTargets) {
+        // Rank targets based on recency of sharing history only for the moment.
+        // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app.
+        Collections.sort(shareTargets, (t1, t2) -> {
+            if (t1.getEventHistory() == null) {
+                return 1;
+            }
+            if (t2.getEventHistory() == null) {
+                return -1;
+            }
+            Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex(
+                    Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+            Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex(
+                    Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+            if (timeSlot1 == null) {
+                return 1;
+            } else if (timeSlot2 == null) {
+                return -1;
+            } else {
+                return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper());
+            }
+        });
+    }
+
+    private List<ShareTarget> getDirectShareTargets() {
         List<ShareTarget> shareTargets = new ArrayList<>();
         List<ShareShortcutInfo> shareShortcuts =
                 getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
 
         for (ShareShortcutInfo shareShortcut : shareShortcuts) {
             ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+            AppTarget appTarget = new AppTarget.Builder(
+                    new AppTargetId(shortcutInfo.getId()),
+                    shortcutInfo)
+                    .setClassName(shareShortcut.getTargetComponent().getClassName())
+                    .build();
             String packageName = shortcutInfo.getPackage();
             int userId = shortcutInfo.getUserId();
             PackageData packageData = getDataManager().getPackage(packageName, userId);
 
-            ConversationData conversationData = null;
+            ConversationInfo conversationInfo = null;
+            EventHistory eventHistory = null;
             if (packageData != null) {
                 String shortcutId = shortcutInfo.getId();
-                ConversationInfo conversationInfo =
-                        packageData.getConversationInfo(shortcutId);
-
+                conversationInfo = packageData.getConversationInfo(shortcutId);
                 if (conversationInfo != null) {
-                    EventHistory eventHistory = packageData.getEventHistory(shortcutId);
-                    conversationData = new ConversationData(
-                            packageName, userId, conversationInfo, eventHistory);
+                    eventHistory = packageData.getEventHistory(shortcutId);
                 }
             }
-            shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+            shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo));
         }
 
         return shareTargets;
     }
 
+    private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) {
+        List<ShareTarget> shareTargets = new ArrayList<>();
+        for (AppTarget target : targets) {
+            PackageData packageData = getDataManager().getPackage(target.getPackageName(),
+                    target.getUser().getIdentifier());
+            shareTargets.add(new ShareTarget(target,
+                    packageData == null ? null
+                            : packageData.getClassLevelEventHistory(target.getClassName()), null));
+        }
+        return shareTargets;
+    }
+
     @VisibleForTesting
     static class ShareTarget {
 
         @NonNull
-        private final ShareShortcutInfo mShareShortcutInfo;
+        private final AppTarget mAppTarget;
         @Nullable
-        private final ConversationData mConversationData;
+        private final EventHistory mEventHistory;
+        @Nullable
+        private final ConversationInfo mConversationInfo;
 
-        private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
-                @Nullable ConversationData conversationData) {
-            mShareShortcutInfo = shareShortcutInfo;
-            mConversationData = conversationData;
+        private ShareTarget(@NonNull AppTarget appTarget,
+                @Nullable EventHistory eventHistory,
+                @Nullable ConversationInfo conversationInfo) {
+            mAppTarget = appTarget;
+            mEventHistory = eventHistory;
+            mConversationInfo = conversationInfo;
         }
 
         @NonNull
         @VisibleForTesting
-        ShareShortcutInfo getShareShortcutInfo() {
-            return mShareShortcutInfo;
+        AppTarget getAppTarget() {
+            return mAppTarget;
         }
 
         @Nullable
         @VisibleForTesting
-        ConversationData getConversationData() {
-            return mConversationData;
+        EventHistory getEventHistory() {
+            return mEventHistory;
+        }
+
+        @Nullable
+        @VisibleForTesting
+        ConversationInfo getConversationInfo() {
+            return mConversationInfo;
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 25d0778..feae1e1 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -29,6 +29,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
+import com.android.server.display.DisplayModeDirector.RefreshRateRange;
+import com.android.server.display.DisplayModeDirector.Vote;
+
+import com.google.common.truth.Truth;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,6 +43,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DisplayModeDirectorTest {
+    // The tolerance within which we consider something approximately equals.
+    private static final float FLOAT_TOLERANCE = 0.01f;
+
     private Context mContext;
 
     @Before
@@ -56,30 +65,22 @@
             modes[i - minFps] = new Display.Mode(
                     /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i);
         }
-        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>();
+        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
         supportedModesByDisplay.put(displayId, modes);
         director.injectSupportedModesByDisplay(supportedModesByDisplay);
-        SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>();
+        SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
         defaultModesByDisplay.put(displayId, modes[0]);
         director.injectDefaultModeByDisplay(defaultModesByDisplay);
         return director;
     }
 
-    private int[] intRange(int min, int max) {
-        int[] range = new int[max - min + 1];
-        for (int i = min; i <= max; i++) {
-            range[i - min] = i;
-        }
-        return range;
-    }
-
     @Test
     public void testDisplayModeVoting() {
         int displayId = 0;
 
         // With no votes present, DisplayModeDirector should allow any refresh rate.
-        assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60,
-                             new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
+        assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/60,
+                             new RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
                 createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs(
                         displayId));
 
@@ -93,20 +94,16 @@
             int maxFps = 90;
             DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
             assertTrue(2 * numPriorities < maxFps - minFps + 1);
-            SparseArray<DisplayModeDirector.Vote> votes =
-                    new SparseArray<DisplayModeDirector.Vote>();
-            SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
-                    new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+            SparseArray<Vote> votes = new SparseArray<>();
+            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
             votesByDisplay.put(displayId, votes);
             for (int i = 0; i < numPriorities; i++) {
-                int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i;
-                votes.put(
-                        priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i));
+                int priority = Vote.MIN_PRIORITY + i;
+                votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i));
                 director.injectVotesByDisplay(votesByDisplay);
-                assertEquals(
-                        new DisplayModeDirector.DesiredDisplayModeSpecs(
+                assertEquals(new DesiredDisplayModeSpecs(
                                 /*baseModeId=*/minFps + i,
-                                new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)),
+                                new RefreshRateRange(minFps + i, maxFps - i)),
                         director.getDesiredDisplayModeSpecs(displayId));
             }
         }
@@ -116,19 +113,35 @@
         {
             assertTrue(numPriorities >= 2);
             DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
-            SparseArray<DisplayModeDirector.Vote> votes =
-                    new SparseArray<DisplayModeDirector.Vote>();
-            SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
-                    new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+            SparseArray<Vote> votes = new SparseArray<>();
+            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
             votesByDisplay.put(displayId, votes);
-            votes.put(DisplayModeDirector.Vote.MAX_PRIORITY,
-                    DisplayModeDirector.Vote.forRefreshRates(65, 85));
-            votes.put(DisplayModeDirector.Vote.MIN_PRIORITY,
-                    DisplayModeDirector.Vote.forRefreshRates(70, 80));
+            votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85));
+            votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80));
             director.injectVotesByDisplay(votesByDisplay);
-            assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70,
-                                 new DisplayModeDirector.RefreshRateRange(70, 80)),
+            assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/70,
+                                 new RefreshRateRange(70, 80)),
                     director.getDesiredDisplayModeSpecs(displayId));
         }
     }
+
+    @Test
+    public void testVotingWithFloatingPointErrors() {
+        int displayId = 0;
+        DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90);
+        SparseArray<Vote> votes = new SparseArray<>();
+        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+        votesByDisplay.put(displayId, votes);
+        float error = FLOAT_TOLERANCE / 4;
+        votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60));
+        votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error));
+        votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE,
+                Vote.forRefreshRates(60 - error, 60 - error));
+        director.injectVotesByDisplay(votesByDisplay);
+        DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+
+        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
index 6fafe11..9b076e8 100644
--- a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
@@ -118,16 +118,6 @@
     }
 
     @Test
-    public void testRequestSetAllowed() {
-        mProvider.requestSetAllowed(true);
-        verify(mRealProvider, times(1)).onRequestSetAllowed(true);
-
-        mProvider.setMockProvider(mMockProvider);
-        mProvider.requestSetAllowed(true);
-        verify(mMockProvider, times(1)).onRequestSetAllowed(true);
-    }
-
-    @Test
     public void testSendExtraCommand() {
         mProvider.sendExtraCommand(0, 0, "command", null);
         verify(mRealProvider, times(1)).onExtraCommand(0, 0, "command", null);
diff --git a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
index 762080f..5943f67 100644
--- a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
@@ -38,8 +38,5 @@
     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
 
     @Override
-    protected void onRequestSetAllowed(boolean allowed) {}
-
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f73a4b5..b54317b 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -100,6 +100,7 @@
     private static final int USER_ID_PRIMARY_MANAGED = 10;
     private static final int USER_ID_SECONDARY = 11;
     private static final String TEST_PKG_NAME = "pkg";
+    private static final String TEST_CLASS_NAME = "class";
     private static final String TEST_SHORTCUT_ID = "sc";
     private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
     private static final String PHONE_NUMBER = "+1234567890";
@@ -206,13 +207,13 @@
         mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
         mDataManager.onUserUnlocked(USER_ID_SECONDARY);
 
-        mDataManager.onShortcutAddedOrUpdated(
+        mDataManager.addOrUpdateConversationInfo(
                 buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
                         buildPerson(true, false)));
-        mDataManager.onShortcutAddedOrUpdated(
+        mDataManager.addOrUpdateConversationInfo(
                 buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
                         buildPerson(false, true)));
-        mDataManager.onShortcutAddedOrUpdated(
+        mDataManager.addOrUpdateConversationInfo(
                 buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
 
         List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -236,9 +237,9 @@
     @Test
     public void testAccessConversationForUnlockedUsersOnly() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-        mDataManager.onShortcutAddedOrUpdated(
+        mDataManager.addOrUpdateConversationInfo(
                 buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
-        mDataManager.onShortcutAddedOrUpdated(
+        mDataManager.addOrUpdateConversationInfo(
                 buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
 
         List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -261,11 +262,12 @@
     }
 
     @Test
-    public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
+    public void testReportAppTargetEvent_directSharing()
+            throws IntentFilter.MalformedMimeTypeException {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
                 .build();
@@ -274,7 +276,55 @@
                         .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
                         .build();
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
-        mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
+        mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+        List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+                Event.SHARE_EVENT_TYPES);
+        assertEquals(1, activeShareTimeSlots.size());
+    }
+
+    @Test
+    public void testReportAppTargetEvent_directSharing_createConversation()
+            throws IntentFilter.MalformedMimeTypeException {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                null);
+        AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+                .build();
+        AppTargetEvent appTargetEvent =
+                new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+                        .build();
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+        mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+        List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+                Event.SHARE_EVENT_TYPES);
+        assertEquals(1, activeShareTimeSlots.size());
+        ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+                .getConversationStore()
+                .getConversation(TEST_SHORTCUT_ID);
+        assertNotNull(conversationInfo);
+        assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
+    }
+
+    @Test
+    public void testReportAppTargetEvent_appSharing()
+            throws IntentFilter.MalformedMimeTypeException {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        AppTarget appTarget = new AppTarget.Builder(
+                    new AppTargetId(TEST_SHORTCUT_ID),
+                    TEST_PKG_NAME,
+                    UserHandle.of(USER_ID_PRIMARY))
+                .setClassName(TEST_CLASS_NAME)
+                .build();
+        AppTargetEvent appTargetEvent =
+                new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+                        .build();
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+        mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
 
         List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
                 Event.SHARE_EVENT_TYPES);
@@ -288,7 +338,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         final String newPhoneNumber = "+1000000000";
         mInjector.mContactsQueryHelper.mIsStarred = true;
@@ -312,7 +362,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         NotificationListenerService listenerService =
                 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -330,7 +380,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         NotificationListenerService listenerService =
                 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -350,7 +400,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         NotificationListenerService listenerService =
                 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -375,7 +425,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         NotificationListenerService listenerService =
                 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -401,7 +451,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         NotificationListenerService listenerService =
                 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -430,7 +480,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting();
         contentObserver.onChange(false);
@@ -453,7 +503,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
         mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
 
         ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting();
@@ -476,7 +526,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
         assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
 
         PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
@@ -493,7 +543,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
         assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
 
         doAnswer(ans -> null).when(mPackageManagerInternal)
@@ -508,7 +558,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
 
         long currentTimestamp = System.currentTimeMillis();
         mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
@@ -529,7 +579,7 @@
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
-        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
         mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
 
         long currentTimestamp = System.currentTimeMillis();
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index f498a94..c6cd347 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -16,16 +16,19 @@
 
 package com.android.server.people.prediction;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anySet;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -33,22 +36,26 @@
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Range;
 
 import com.android.server.people.data.ConversationInfo;
 import com.android.server.people.data.DataManager;
 import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.EventIndex;
 import com.android.server.people.data.PackageData;
-import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 @RunWith(JUnit4.class)
 public final class ShareTargetPredictorTest {
@@ -57,17 +64,32 @@
     private static final int NUM_PREDICTED_TARGETS = 5;
     private static final int USER_ID = 0;
     private static final String PACKAGE_1 = "pkg1";
-    private static final String CLASS_1 = "cls1";
     private static final String PACKAGE_2 = "pkg2";
+    private static final String PACKAGE_3 = "pkg3";
+    private static final String CLASS_1 = "cls1";
     private static final String CLASS_2 = "cls2";
 
     @Mock private Context mContext;
     @Mock private DataManager mDataManager;
+    @Mock private Consumer<List<AppTarget>> mUpdatePredictionsMethod;
     @Mock private PackageData mPackageData1;
     @Mock private PackageData mPackageData2;
+    @Mock private EventHistory mEventHistory1;
+    @Mock private EventHistory mEventHistory2;
+    @Mock private EventHistory mEventHistory3;
+    @Mock private EventHistory mEventHistory4;
+    @Mock private EventHistory mEventHistory5;
+    @Mock private EventHistory mEventHistory6;
+
+    @Mock private EventIndex mEventIndex1;
+    @Mock private EventIndex mEventIndex2;
+    @Mock private EventIndex mEventIndex3;
+    @Mock private EventIndex mEventIndex4;
+    @Mock private EventIndex mEventIndex5;
+    @Mock private EventIndex mEventIndex6;
+    @Captor private ArgumentCaptor<List<AppTarget>> mAppTargetCaptor;
 
     private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
-
     private ShareTargetPredictor mPredictor;
 
     @Before
@@ -84,11 +106,11 @@
                 .setExtras(new Bundle())
                 .build();
         mPredictor = new ShareTargetPredictor(
-                predictionContext, targets -> { }, mDataManager, USER_ID);
+                predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
     }
 
     @Test
-    public void testGetShareTargets() {
+    public void testPredictTargets() {
         mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
         mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
         mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
@@ -99,24 +121,148 @@
         when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
         // "sc4" does not have a ConversationInfo.
 
-        when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
-        when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
+        when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+        when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+        when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
 
-        List<ShareTarget> shareTargets = mPredictor.getShareTargets();
+        mPredictor.predictTargets();
 
-        assertEquals(4, shareTargets.size());
+        verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+        List<AppTarget> res = mAppTargetCaptor.getValue();
+        assertEquals(4, res.size());
 
-        assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
-        assertNotNull(shareTargets.get(0).getConversationData());
+        assertEquals("sc3", res.get(0).getId().getId());
+        assertEquals(CLASS_2, res.get(0).getClassName());
+        assertEquals(PACKAGE_2, res.get(0).getPackageName());
 
-        assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
-        assertNotNull(shareTargets.get(1).getConversationData());
+        assertEquals("sc2", res.get(1).getId().getId());
+        assertEquals(CLASS_1, res.get(1).getClassName());
+        assertEquals(PACKAGE_1, res.get(1).getPackageName());
 
-        assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
-        assertNotNull(shareTargets.get(2).getConversationData());
+        assertEquals("sc1", res.get(2).getId().getId());
+        assertEquals(CLASS_1, res.get(2).getClassName());
+        assertEquals(PACKAGE_1, res.get(2).getPackageName());
 
-        assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
-        assertNull(shareTargets.get(3).getConversationData());
+        assertEquals("sc4", res.get(3).getId().getId());
+        assertEquals(CLASS_2, res.get(3).getClassName());
+        assertEquals(PACKAGE_2, res.get(3).getPackageName());
+    }
+
+    @Test
+    public void testPredictTargets_reachTargetsLimit() {
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5"));
+        mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6"));
+
+        when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+        when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+        when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+        when(mPackageData2.getConversationInfo("sc4")).thenReturn(mock(ConversationInfo.class));
+        when(mPackageData1.getConversationInfo("sc5")).thenReturn(mock(ConversationInfo.class));
+        when(mPackageData2.getConversationInfo("sc6")).thenReturn(mock(ConversationInfo.class));
+
+        when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+        when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+        when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+        when(mPackageData2.getEventHistory("sc4")).thenReturn(mEventHistory4);
+        when(mPackageData1.getEventHistory("sc5")).thenReturn(mEventHistory5);
+        when(mPackageData2.getEventHistory("sc6")).thenReturn(mEventHistory6);
+
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6);
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
+        when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(5L, 6L));
+        when(mEventIndex6.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(6L, 7L));
+
+        mPredictor.predictTargets();
+
+        verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+        List<AppTarget> res = mAppTargetCaptor.getValue();
+        assertEquals(5, res.size());
+
+        assertEquals("sc6", res.get(0).getId().getId());
+        assertEquals(CLASS_2, res.get(0).getClassName());
+        assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+        assertEquals("sc5", res.get(1).getId().getId());
+        assertEquals(CLASS_1, res.get(1).getClassName());
+        assertEquals(PACKAGE_1, res.get(1).getPackageName());
+
+        assertEquals("sc4", res.get(2).getId().getId());
+        assertEquals(CLASS_2, res.get(2).getClassName());
+        assertEquals(PACKAGE_2, res.get(2).getPackageName());
+
+        assertEquals("sc3", res.get(3).getId().getId());
+        assertEquals(CLASS_2, res.get(3).getClassName());
+        assertEquals(PACKAGE_2, res.get(3).getPackageName());
+
+        assertEquals("sc2", res.get(4).getId().getId());
+        assertEquals(CLASS_1, res.get(4).getClassName());
+        assertEquals(PACKAGE_1, res.get(4).getPackageName());
+    }
+
+    @Test
+    public void testSortTargets() {
+        AppTarget appTarget1 = new AppTarget.Builder(
+                    new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+                .setClassName(CLASS_1)
+                .build();
+        AppTarget appTarget2 = new AppTarget.Builder(
+                    new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+                .setClassName(CLASS_2)
+                .build();
+        AppTarget appTarget3 = new AppTarget.Builder(
+                    new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                .setClassName(CLASS_1)
+                .build();
+        AppTarget appTarget4 = new AppTarget.Builder(
+                    new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                .setClassName(CLASS_2)
+                .build();
+        AppTarget appTarget5 = new AppTarget.Builder(
+                new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
+                .setClassName(CLASS_1)
+                .build();
+
+        when(mPackageData1.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory1);
+        when(mPackageData1.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory2);
+        when(mPackageData2.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory3);
+        when(mPackageData2.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory4);
+        // PackageData of PACKAGE_3 is empty.
+        when(mDataManager.getPackage(PACKAGE_3, USER_ID)).thenReturn(null);
+
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
+
+        mPredictor.sortTargets(
+                List.of(appTarget1, appTarget2, appTarget3, appTarget4, appTarget5),
+                mUpdatePredictionsMethod);
+
+        verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+        assertThat(mAppTargetCaptor.getValue()).containsExactly(
+                appTarget4, appTarget3, appTarget2, appTarget1, appTarget5);
     }
 
     private ShareShortcutInfo buildShareShortcut(
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 3f0cda3..b7199f7 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -29,6 +29,7 @@
     libs: [
         "android.test.runner",
         "android.test.base",
+        "android.test.mock",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7f9732b..5829961 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -36,9 +36,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -67,6 +68,7 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.service.notification.ConversationChannelWrapper;
+import android.test.mock.MockIContentProvider;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableContentResolver;
 import android.util.ArrayMap;
@@ -87,6 +89,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
@@ -123,7 +126,7 @@
     @Mock NotificationUsageStats mUsageStats;
     @Mock RankingHandler mHandler;
     @Mock PackageManager mPm;
-    @Mock IContentProvider mTestIContentProvider;
+    @Spy IContentProvider mTestIContentProvider = new MockIContentProvider();
     @Mock Context mContext;
     @Mock ZenModeHelper mMockZenModeHelper;
 
@@ -170,12 +173,12 @@
         when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
         contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
 
-        when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
-                .thenReturn(CANONICAL_SOUND_URI);
-        when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(CANONICAL_SOUND_URI);
-        when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(SOUND_URI);
+        doReturn(CANONICAL_SOUND_URI)
+                .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+        doReturn(CANONICAL_SOUND_URI)
+                .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+        doReturn(SOUND_URI)
+                .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
 
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -506,12 +509,13 @@
                 .appendQueryParameter("title", "Test")
                 .appendQueryParameter("canonical", "1")
                 .build();
-        when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(canonicalBasedOnLocal);
-        when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(localUri);
-        when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal)))
-                .thenReturn(localUri);
+        doReturn(canonicalBasedOnLocal)
+                .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+        doReturn(localUri)
+                .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+        doReturn(localUri)
+                .when(mTestIContentProvider).uncanonicalize(any(), any(),
+                eq(canonicalBasedOnLocal));
 
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -530,10 +534,10 @@
     @Test
     public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
         Thread.sleep(3000);
-        when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(null);
-        when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
-                .thenReturn(null);
+        doReturn(null)
+                .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+        doReturn(null)
+                .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
 
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -557,7 +561,8 @@
     @Test
     public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
         // Not a local uncanonicalized uri, simulating that it fails to exist locally
-        when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null);
+        doReturn(null)
+                .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
         String id = "id";
         String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a0ea729..c9c3649 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1025,9 +1025,9 @@
     public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
         final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
-        homeStack.forAllTasks((t) -> {
+        homeStack.forAllLeafTasks((t) -> {
             homeStack.removeChild(t, "test");
-        }, true /* traverseTopToBottom */, homeStack);
+        }, true /* traverseTopToBottom */);
         mActivity.finishing = true;
         doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
         spyOn(mStack);
@@ -1051,9 +1051,9 @@
     public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
         // Empty the home stack.
         final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
-        homeStack.forAllTasks((t) -> {
+        homeStack.forAllLeafTasks((t) -> {
             homeStack.removeChild(t, "test");
-        }, true /* traverseTopToBottom */, homeStack);
+        }, true /* traverseTopToBottom */);
         mActivity.finishing = true;
         spyOn(mStack);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1a8f2a6..b3c6b22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -27,6 +27,7 @@
 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -998,12 +999,10 @@
     public void testApplyTopFixedRotationTransform() {
         mWm.mIsFixedRotationTransformEnabled = true;
         final Configuration config90 = new Configuration();
-        mDisplayContent.getDisplayRotation().setRotation(ROTATION_90);
-        mDisplayContent.computeScreenConfiguration(config90);
-        mDisplayContent.onRequestedOverrideConfigurationChanged(config90);
+        mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
 
         final Configuration config = new Configuration();
-        mDisplayContent.getDisplayRotation().setRotation(Surface.ROTATION_0);
+        mDisplayContent.getDisplayRotation().setRotation(ROTATION_0);
         mDisplayContent.computeScreenConfiguration(config);
         mDisplayContent.onRequestedOverrideConfigurationChanged(config);
 
@@ -1014,11 +1013,18 @@
         app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         assertTrue(app.isFixedRotationTransforming());
+        assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly(
+                ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
+                false /* forceUpdate */));
+        // The display should keep current orientation and the rotated configuration should apply
+        // to the activity.
         assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation);
         assertEquals(config90.orientation, app.getConfiguration().orientation);
+        assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
 
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
 
+        // The display should be rotated after the launch is finished.
         assertFalse(app.hasFixedRotationTransform());
         assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index dd46673..ec20262 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -313,8 +313,8 @@
      */
     @Test
     public void testResizeDockedStackForSplitScreenPrimary() {
-        final Rect taskSize = new Rect(0, 0, 1000, 1000);
-        final Rect stackSize = new Rect(0, 0, 300, 300);
+        final Rect configSize = new Rect(0, 0, 1000, 1000);
+        final Rect displayedSize = new Rect(0, 0, 300, 300);
 
         // Create primary split-screen stack with a task.
         final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer)
@@ -325,11 +325,13 @@
         final Task task = primaryStack.getTopMostTask();
 
         // Resize dock stack.
-        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+        mService.resizeDockedStack(displayedSize, configSize, null, null, null);
 
         // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(stackSize, primaryStack.getBounds());
-        assertEquals(taskSize, task.getBounds());
+        assertEquals(displayedSize, primaryStack.getDisplayedBounds());
+        assertEquals(displayedSize, primaryStack.getDisplayedBounds());
+        assertEquals(configSize, primaryStack.getBounds());
+        assertEquals(configSize, task.getBounds());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index cd53ece..45b51cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,6 +28,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -300,6 +301,7 @@
         Rect newSize = new Rect(10, 10, 300, 300);
         Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
         c.windowConfiguration.setBounds(newSize);
+        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
         tile1.onRequestedOverrideConfigurationChanged(c);
         assertEquals(newSize, stack.getBounds());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 6e4be88..6387a3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -16,9 +16,11 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -33,6 +35,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.clearInvocations;
 
+import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
@@ -184,6 +187,16 @@
         doReturn(stackOutset).when(stack).getStackOutset();
         doReturn(true).when(stack).inMultiWindowMode();
 
+        // Mock the resolved override windowing mode to non-fullscreen
+        final WindowConfiguration windowConfiguration =
+                stack.getResolvedOverrideConfiguration().windowConfiguration;
+        spyOn(windowConfiguration);
+        doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+                .when(windowConfiguration).getWindowingMode();
+
+        // Prevent adjust task dimensions
+        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
+
         final Rect stackBounds = new Rect(200, 200, 800, 1000);
         // Update surface position and size by the given bounds.
         stack.setBounds(stackBounds);
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index f7ec11c..d1d64d3 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -156,6 +156,12 @@
         }
 
         @Override
+        public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+                RemoteCallback callback) {
+            MockContentProvider.this.canonicalizeAsync(uri, callback);
+        }
+
+        @Override
         public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
                 throws RemoteException {
             return MockContentProvider.this.uncanonicalize(uri);
@@ -292,6 +298,18 @@
     /**
      * @hide
      */
+    @SuppressWarnings("deprecation")
+    public void canonicalizeAsync(Uri uri, RemoteCallback callback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            final Bundle bundle = new Bundle();
+            bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, canonicalize(uri));
+            callback.sendResult(bundle);
+        });
+    }
+
+    /**
+     * @hide
+     */
     public boolean refresh(Uri url, Bundle args) {
         throw new UnsupportedOperationException("unimplemented mock method call");
     }
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 359c448..2c66047 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -834,6 +834,12 @@
 
     /** @hide */
     @Override
+    public Display getDisplayNoVerify() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
     public int getDisplayId() {
         throw new UnsupportedOperationException();
     }
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 1831bcd..223bcc5 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -145,12 +145,23 @@
     }
 
     @Override
-    public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
-            throws RemoteException {
+    public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
     @Override
+    @SuppressWarnings("deprecation")
+    public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+            RemoteCallback remoteCallback) {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            final Bundle bundle = new Bundle();
+            bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+                    canonicalize(callingPkg, featureId, uri));
+            remoteCallback.sendResult(bundle);
+        });
+    }
+
+    @Override
     public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
             throws RemoteException {
         throw new UnsupportedOperationException("unimplemented mock method");
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e1450cb..1a12af3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2167,6 +2167,7 @@
         sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
                 append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
                 append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
+                .append(" HOME-PROVIDER-NETWORK: ").append(this.isHomeProviderNetwork)
                 .append(" PRIO: ").append(this.priority)
                 .append(" HIDDEN: ").append(this.hiddenSSID)
                 .append(" PMF: ").append(this.requirePmf)