Merge "Fix inconsistent naming and indentation."
diff --git a/api/current.txt b/api/current.txt
index 343fbd9..574f138 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11375,6 +11375,7 @@
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
+    method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender);
     method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
@@ -38833,6 +38834,7 @@
     field public static final String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
     field public static final String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
+    field public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS = "android.settings.NOTIFICATION_ASSISTANT_SETTINGS";
     field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
     field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
diff --git a/api/system-current.txt b/api/system-current.txt
index b7f435a..b448f61 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1593,8 +1593,8 @@
     method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 4deb8bd..69fbf1f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1200,9 +1200,9 @@
 
     userid_t userId = multiuser_get_user_id(uid);
 
-    bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING;
-    bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
-    bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
+    bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
+    bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED;
+    bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
 
     ProtoOutputStream proto;
     for (const auto& expId : experimentIds) {
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 0a26bfb..d2535c9 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -32983,7 +32983,7 @@
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getInTouchMode()Z
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getWindowId(Landroid/os/IBinder;)Landroid/view/IWindowId;
 HSPLandroid/view/IWindowSession$Stub$Proxy;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
-HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(IZ)Z
 HSPLandroid/view/IWindowSession$Stub$Proxy;->pokeDrawLock(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 HSPLandroid/view/IWindowSession$Stub$Proxy;->remove(Landroid/view/IWindow;)V
@@ -33007,7 +33007,7 @@
 HSPLandroid/view/IWindowSession;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
 HSPLandroid/view/IWindowSession;->outOfMemory(Landroid/view/IWindow;)Z
 HSPLandroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder;
-HSPLandroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+HSPLandroid/view/IWindowSession;->performHapticFeedback(IZ)Z
 HSPLandroid/view/IWindowSession;->pokeDrawLock(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowSession;->prepareToReplaceWindows(Landroid/os/IBinder;Z)V
 HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 2abb631..aaff76e 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1523,7 +1523,7 @@
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->getInTouchMode()Z
 Landroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder;
-Landroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+Landroid/view/IWindowSession;->performHapticFeedback(IZ)Z
 Landroid/view/IWindowSession;->remove(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->setInTouchMode(Z)V
 Landroid/view/IWindowSession;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a63350c..91fc188 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2387,8 +2387,8 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
-            notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         }
+        notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         mEnterAnimationComplete = false;
     }
 
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index a251c00..0cf83fd 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -50,5 +50,8 @@
     void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags,
             in IntentSender statusReceiver, int userId);
 
+    void installExistingPackage(String packageName, int installFlags, int installReason,
+            in IntentSender statusReceiver, int userId);
+
     void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index badd48f..6302071 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -590,6 +590,30 @@
         }
     }
 
+    /**
+     * Install the given package, which already exists on the device, for the user for which this
+     * installer was created.
+     *
+     * @param packageName The package to install.
+     * @param installReason Reason for install.
+     * @param statusReceiver Where to deliver the result.
+     */
+    @RequiresPermission(allOf = {
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.INSTALL_EXISTING_PACKAGES})
+    public void installExistingPackage(@NonNull String packageName,
+            @InstallReason int installReason,
+            @Nullable IntentSender statusReceiver) {
+        Preconditions.checkNotNull(packageName, "packageName cannot be null");
+        try {
+            mInstaller.installExistingPackage(packageName, 0, installReason, statusReceiver,
+                    mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
     /** {@hide} */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6b2442f..5e70ecb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5122,7 +5122,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the calling user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @SystemApi
     public abstract int installExistingPackage(String packageName) throws NameNotFoundException;
 
@@ -5130,7 +5133,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the calling user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @SystemApi
     public abstract int installExistingPackage(String packageName, @InstallReason int installReason)
             throws NameNotFoundException;
@@ -5139,7 +5145,10 @@
      * If there is already an application with the given package name installed
      * on the system for other users, also install it for the specified user.
      * @hide
+     *
+     * @deprecated use {@link PackageInstaller#installExistingPackage()} instead.
      */
+    @Deprecated
     @RequiresPermission(anyOf = {
             Manifest.permission.INSTALL_EXISTING_PACKAGES,
             Manifest.permission.INSTALL_PACKAGES,
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 47b1eef..650d2178 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -664,8 +664,15 @@
           .append(abi);
         final String paths = sb.toString();
 
-        if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
-        setDriverPath(paths);
+        final String sphalLibraries =
+                coreSettings.getString(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES);
+
+        if (DEBUG) {
+            Log.v(TAG,
+                    "gfx driver package search path: " + paths
+                            + ", required sphal libraries: " + sphalLibraries);
+        }
+        setDriverPathAndSphalLibraries(paths, sphalLibraries);
 
         if (driverAppInfo.metaData == null) {
             throw new NullPointerException("apk's meta-data cannot be null");
@@ -700,7 +707,7 @@
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
     private static native void setDebugLayersGLES(String layers);
-    private static native void setDriverPath(String path);
+    private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
     private static native void setGpuStats(String driverPackageName, String driverVersionName,
             long driverVersionCode, long driverBuildTime, String appPackageName);
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index cd7bbfd..9d58064 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -519,7 +519,7 @@
         }
 
         @Override
-        public void scheduleRequest(@NonNull PendingRequest<RemoteService,
+        public void scheduleRequest(@NonNull BasePendingRequest<RemoteService,
                 IPermissionController> pendingRequest) {
             super.scheduleRequest(pendingRequest);
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1530626..17a0831 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1204,6 +1204,21 @@
     public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
 
     /**
+     * Activity Action: Show Notification assistant settings.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     * @see android.service.notification.NotificationAssistantService
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS =
+            "android.settings.NOTIFICATION_ASSISTANT_SETTINGS";
+
+    /**
      * Activity Action: Show Notification listener settings.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 658f06a..240aad5 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -148,7 +148,7 @@
     void setInTouchMode(boolean showFocus);
     boolean getInTouchMode();
 
-    boolean performHapticFeedback(IWindow window, int effectId, boolean always);
+    boolean performHapticFeedback(int effectId, boolean always);
 
     /**
      * Initiate the drag operation itself
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 89c6703..be6b56c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6996,7 +6996,7 @@
     @Override
     public boolean performHapticFeedback(int effectId, boolean always) {
         try {
-            return mWindowSession.performHapticFeedback(mWindow, effectId, always);
+            return mWindowSession.performHapticFeedback(effectId, always);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 5d59e42..87e18b7 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -424,28 +424,20 @@
      *
      * @param nodes The nodes in the hosting window.
      * @param rootNodeId The id of the root to evict.
-     *
-     * @return {@code true} if the cache was cleared
      */
-    private boolean clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+    private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
             long rootNodeId) {
         AccessibilityNodeInfo current = nodes.get(rootNodeId);
         if (current == null) {
-            // The node isn't in the cache, but its descendents might be.
-            clear();
-            return true;
+            return;
         }
         nodes.remove(rootNodeId);
         final int childCount = current.getChildCount();
         for (int i = 0; i < childCount; i++) {
             final long childNodeId = current.getChildId(i);
-            if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
-                current.recycle();
-                return true;
-            }
+            clearSubTreeRecursiveLocked(nodes, childNodeId);
         }
         current.recycle();
-        return false;
     }
 
     /**
diff --git a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
index 8faae1f..d4b7e85 100644
--- a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
+++ b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java
@@ -40,8 +40,15 @@
             final Class<InspectionCompanion<T>> companionClass =
                     (Class<InspectionCompanion<T>>) cls.getClassLoader().loadClass(companionName);
             return companionClass.newInstance();
-        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+        } catch (ClassNotFoundException e) {
             return null;
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InstantiationException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof RuntimeException) throw (RuntimeException) cause;
+            if (cause instanceof Error) throw (Error) cause;
+            throw new RuntimeException(cause);
         }
     }
 }
diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
index 26cf180..293ffd3 100644
--- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java
@@ -39,7 +39,7 @@
 
     private final int mInitialCapacity;
 
-    protected ArrayList<PendingRequest<S, I>> mPendingRequests;
+    protected ArrayList<BasePendingRequest<S, I>> mPendingRequests;
 
     public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
             @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
@@ -85,7 +85,7 @@
     }
 
     @Override // from AbstractRemoteService
-    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
+    void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (mPendingRequests == null) {
             mPendingRequests = new ArrayList<>(mInitialCapacity);
         }
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index a937aa7..732553b 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -95,7 +95,7 @@
     private long mNextUnbind;
 
     /** Requests that have been scheduled, but that are not finished yet */
-    private final ArrayList<PendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
+    private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
 
     /**
      * Callback called when the service dies.
@@ -183,8 +183,15 @@
 
     /**
      * Defines how long after we make a remote request to a fill service we timeout.
+     *
+     * <p>Just need to be overridden by subclasses that uses sync {@link PendingRequest}s.
+     *
+     * @throws UnsupportedOperationException if called when not overridden.
+     *
      */
-    protected abstract long getRemoteRequestMillis();
+    protected long getRemoteRequestMillis() {
+        throw new UnsupportedOperationException("not implemented by " + getClass());
+    }
 
     /**
      * Gets the currently registered service interface or {@code null} if the service is not
@@ -243,7 +250,7 @@
         pw.append(prefix).append(tab).append("destroyed=")
                 .append(String.valueOf(mDestroyed)).println();
         pw.append(prefix).append(tab).append("numUnfinishedRequests=")
-                .append(String.valueOf(mUnfinishedRequests.size()));
+                .append(String.valueOf(mUnfinishedRequests.size())).println();
         final boolean bound = handleIsBound();
         pw.append(prefix).append(tab).append("bound=")
                 .append(String.valueOf(bound));
@@ -260,9 +267,13 @@
         pw.println();
         pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
         pw.append(prefix).append("idleTimeout=")
-            .append(Long.toString(idleTimeout / 1000)).append("s").println();
-        pw.append(prefix).append("requestTimeout=")
-            .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
+            .append(Long.toString(idleTimeout / 1000)).append("s\n");
+        pw.append(prefix).append("requestTimeout=");
+        try {
+            pw.append(Long.toString(getRemoteRequestMillis() / 1000)).append("s\n");
+        } catch (UnsupportedOperationException e) {
+            pw.append("not supported\n");
+        }
         pw.println();
     }
 
@@ -273,7 +284,7 @@
      * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the
      * service doesn't respond.
      */
-    protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+    protected void scheduleRequest(@NonNull BasePendingRequest<S, I> pendingRequest) {
         mHandler.sendMessage(obtainMessage(
                 AbstractRemoteService::handlePendingRequest, this, pendingRequest));
     }
@@ -283,12 +294,12 @@
      *
      * @param finshedRequest The request that is finished
      */
-    void finishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+    void finishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) {
         mHandler.sendMessage(
                 obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest));
     }
 
-    private void handleFinishRequest(@NonNull PendingRequest<S, I> finshedRequest) {
+    private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) {
         mUnfinishedRequests.remove(finshedRequest);
 
         if (mUnfinishedRequests.isEmpty()) {
@@ -361,7 +372,7 @@
      * Handles a request, either processing it right now when bound, or saving it to be handled when
      * bound.
      */
-    protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) {
+    protected final void handlePendingRequest(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (checkIfDestroyed() || mCompleted) return;
 
         if (!handleIsBound()) {
@@ -384,7 +395,8 @@
     /**
      * Defines what to do with a request that arrives while not bound to the service.
      */
-    abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest);
+    abstract void handlePendingRequestWhileUnBound(
+            @NonNull BasePendingRequest<S, I> pendingRequest);
 
     private boolean handleIsBound() {
         return mService != null;
@@ -471,50 +483,28 @@
     /**
      * Base class for the requests serviced by the remote service.
      *
-     * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
-     * communicate back with the system server. For cases where that's not needed, you should use
-     * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+     * <p><b>NOTE: </b> this class is not used directly, you should either override
+     * {@link com.android.internal.infra.AbstractRemoteService.PendingRequest} for sync requests, or
+     * use {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} for async requests.
      *
      * @param <S> the remote service class
      * @param <I> the interface of the binder service
      */
-    public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+    public abstract static class BasePendingRequest<S extends AbstractRemoteService<S, I>,
             I extends IInterface> implements Runnable {
         protected final String mTag = getClass().getSimpleName();
         protected final Object mLock = new Object();
 
-        private final WeakReference<S> mWeakService;
-        private final Runnable mTimeoutTrigger;
-        private final Handler mServiceHandler;
+        final WeakReference<S> mWeakService;
 
         @GuardedBy("mLock")
-        private boolean mCancelled;
+        boolean mCancelled;
 
         @GuardedBy("mLock")
-        private boolean mCompleted;
+        boolean mCompleted;
 
-        protected PendingRequest(@NonNull S service) {
+        BasePendingRequest(@NonNull S service) {
             mWeakService = new WeakReference<>(service);
-            mServiceHandler = service.mHandler;
-            mTimeoutTrigger = () -> {
-                synchronized (mLock) {
-                    if (mCancelled) {
-                        return;
-                    }
-                    mCompleted = true;
-                }
-
-                final S remoteService = mWeakService.get();
-                if (remoteService != null) {
-                    // TODO(b/117779333): we should probably ignore it if service is destroyed.
-                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
-                    onTimeout(remoteService);
-                } else {
-                    Slog.w(mTag, "timed out (no service)");
-                }
-            };
-            mServiceHandler.postAtTime(mTimeoutTrigger,
-                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
         }
 
         /**
@@ -543,10 +533,13 @@
                 service.finishRequest(this);
             }
 
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            onFinished();
+
             return true;
         }
 
+        void onFinished() { }
+
         /**
          * Checks whether this request was cancelled.
          */
@@ -568,15 +561,11 @@
                 mCancelled = true;
             }
 
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            onCancel();
             return true;
         }
 
-        /**
-         * Called by the self-destruct timeout when the remote service didn't reply to the
-         * request on time.
-         */
-        protected abstract void onTimeout(S remoteService);
+        void onCancel() {}
 
         /**
          * Checks whether this request leads to a final state where no other requests can be made.
@@ -587,6 +576,67 @@
     }
 
     /**
+     * Base class for the requests serviced by the remote service.
+     *
+     * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to
+     * communicate back with the system server. For cases where that's not needed, you should use
+     * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead.
+     *
+     * <p><b>NOTE: </b> you must override {@link AbstractRemoteService#getRemoteRequestMillis()},
+     * otherwise the constructor will throw an {@link UnsupportedOperationException}.
+     *
+     * @param <S> the remote service class
+     * @param <I> the interface of the binder service
+     */
+    public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>,
+            I extends IInterface> extends BasePendingRequest<S, I> {
+
+        private final Runnable mTimeoutTrigger;
+        private final Handler mServiceHandler;
+
+        protected PendingRequest(S service) {
+            super(service);
+            mServiceHandler = service.mHandler;
+
+            mTimeoutTrigger = () -> {
+                synchronized (mLock) {
+                    if (mCancelled) {
+                        return;
+                    }
+                    mCompleted = true;
+                }
+
+                final S remoteService = mWeakService.get();
+                if (remoteService != null) {
+                    // TODO(b/117779333): we should probably ignore it if service is destroyed.
+                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
+                    onTimeout(remoteService);
+                } else {
+                    Slog.w(mTag, "timed out (no service)");
+                }
+            };
+            mServiceHandler.postAtTime(mTimeoutTrigger,
+                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
+        }
+
+        @Override
+        final void onFinished() {
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+        }
+
+        @Override
+        final void onCancel() {
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+        }
+
+        /**
+         * Called by the self-destruct timeout when the remote service didn't reply to the
+         * request on time.
+         */
+        protected abstract void onTimeout(S remoteService);
+    }
+
+    /**
      * Represents a request that does not expect a callback from the remote service.
      *
      * @param <I> the interface of the binder service
@@ -600,7 +650,7 @@
     }
 
     private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>,
-            I extends IInterface> extends PendingRequest<S, I> {
+            I extends IInterface> extends BasePendingRequest<S, I> {
         private static final String TAG = MyAsyncPendingRequest.class.getSimpleName();
 
         private final AsyncRequest<I> mRequest;
@@ -623,12 +673,5 @@
                 finish();
             }
         }
-
-        @Override
-        protected void onTimeout(S remoteService) {
-            // TODO(b/117779333): should not happen because we called finish() on run(), although
-            // currently it might be called if the service is destroyed while showing it.
-            Slog.w(TAG, "AsyncPending requested timed out");
-        }
     }
 }
diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
index f0c2233..3e92a0b 100644
--- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java
@@ -38,7 +38,7 @@
         extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface>
         extends AbstractRemoteService<S, I> {
 
-    protected PendingRequest<S, I> mPendingRequest;
+    protected BasePendingRequest<S, I> mPendingRequest;
 
     public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
             @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
@@ -51,7 +51,7 @@
     @Override // from AbstractRemoteService
     void handlePendingRequests() {
         if (mPendingRequest != null) {
-            final PendingRequest<S, I> pendingRequest = mPendingRequest;
+            final BasePendingRequest<S, I> pendingRequest = mPendingRequest;
             mPendingRequest = null;
             handlePendingRequest(pendingRequest);
         }
@@ -73,7 +73,7 @@
     }
 
     @Override // from AbstractRemoteService
-    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) {
+    void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) {
         if (mPendingRequest != null) {
             if (mVerbose) {
                 Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index d9d3cdf..72e3d349 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -27,9 +27,12 @@
     return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries();
 }
 
-void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
+void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring path,
+                                           jstring sphalLibraries) {
     ScopedUtfChars pathChars(env, path);
-    android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
+    ScopedUtfChars sphalLibrariesChars(env, sphalLibraries);
+    android::GraphicsEnv::getInstance().setDriverPathAndSphalLibraries(pathChars.c_str(),
+                                                                       sphalLibrariesChars.c_str());
 }
 
 void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
@@ -84,7 +87,7 @@
 
 const JNINativeMethod g_methods[] = {
     { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
-    { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+    { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
     { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) },
     { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) },
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 5497b86..f74fc21 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -1622,6 +1622,10 @@
     // OPEN: Settings > Apps > Default Apps > Default sms
     DEFAULT_SMS_PICKER = 789;
 
+    // OPEN: Settings > Apps > Notification > Notification Assistant
+    DEFAULT_NOTIFICATION_ASSISTANT = 790;
+
+
     // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
     DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791;
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 93068ea9..3c7b36d 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -38,6 +38,8 @@
     <style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Material.Button.Inset"/>
     <style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Material.Button.Toggle"/>
     <style name="Widget.DeviceDefault.Button.Colored" parent="Widget.Material.Button.Colored">
+        <item name="outlineAmbientShadowColor">@color/btn_colored_background_material</item>
+        <item name="outlineSpotShadowColor">@color/btn_colored_background_material</item>
         <item name="textAppearance">?attr/textAppearanceButton</item>
         <item name="textColor">@color/btn_colored_text_material</item>
     </style>
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index a88968b..e3852e1 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -301,26 +301,6 @@
     }
 
     @Test
-    public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
-        AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
-        long id = nodeInfo.getSourceNodeId();
-        mAccessibilityCache.add(nodeInfo);
-        nodeInfo.recycle();
-
-        AccessibilityEvent event = AccessibilityEvent
-                .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-        event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
-        event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
-
-        mAccessibilityCache.onAccessibilityEvent(event);
-        AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
-        if (shouldBeNull != null) {
-            shouldBeNull.recycle();
-        }
-        assertNull(shouldBeNull);
-    }
-
-    @Test
     public void scrollEvent_clearsNodeAndChild() {
         AccessibilityEvent event = AccessibilityEvent
                 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
index 28b05396..82e33cc 100644
--- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
+++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java
@@ -46,8 +46,6 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 
-
-
 /**
  * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2.
  */
@@ -64,6 +62,7 @@
     private WebView mWebView;
     private SwipeRefreshLayout mSwipeRefreshLayout;
     private ProgressBar mProgressBar;
+    private boolean mForceDisconnect = true;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -141,6 +140,7 @@
                 if (DBG) {
                     Log.d(TAG, "Lost for the current Network, close the browser");
                 }
+                mForceDisconnect = false; // It is already disconnected.
                 if (mNetwork.equals(network)) {
                     finishAndRemoveTask();
                 }
@@ -193,12 +193,6 @@
                 mWebView.goBack();
                 return true;
             }
-
-            // In case of back key, it needs to disconnect current connection with OSU AP to
-            // abort current Provisioning flow.
-            if (mWifiManager != null) {
-                mWifiManager.disconnect();
-            }
         }
         return super.onKeyDown(keyCode, event);
     }
@@ -207,6 +201,11 @@
     protected void onDestroy() {
         if (mNetworkCallback != null) {
             mCm.unregisterNetworkCallback(mNetworkCallback);
+            mNetworkCallback = null;
+        }
+        if (mWifiManager != null && mForceDisconnect) {
+            mWifiManager.disconnect();
+            mWifiManager = null;
         }
         super.onDestroy();
     }
@@ -232,6 +231,7 @@
 
     private class OsuWebViewClient extends WebViewClient {
         boolean mPageError = false;
+        boolean mRedirectResponseReceived = false;
 
         @Override
         public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
@@ -247,6 +247,10 @@
 
             // Do not show the page error on UI.
             if (mPageError) {
+                if (mRedirectResponseReceived) {
+                    // Do not disconnect current connection while provisioning is in progress.
+                    mForceDisconnect = false;
+                }
                 finishAndRemoveTask();
             }
         }
@@ -255,6 +259,7 @@
         public void onReceivedError(WebView view, WebResourceRequest request,
                 WebResourceError error) {
             if (request.getUrl().toString().startsWith("http://127.0.0.1")) {
+                mRedirectResponseReceived = true;
                 view.stopLoading();
             }
 
@@ -266,5 +271,4 @@
             }
          }
     }
-
 }
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
index 330049f..6e95a0e 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -90,6 +90,7 @@
     private int mHeaderTitleRes;
     private int mHeaderDetailsRes;
     private int mHeaderEmptyRes;
+    private CharSequence mHeaderDetails;
     private View.OnClickListener mDetailsOnClickListener;
 
     /**
@@ -148,6 +149,14 @@
     }
 
     /**
+     * Set the text for app entities header details.
+     */
+    public AppEntitiesHeaderController setHeaderDetails(CharSequence detailsText) {
+        mHeaderDetails = detailsText;
+        return this;
+    }
+
+    /**
      * Register a callback to be invoked when header details view is clicked.
      */
     public AppEntitiesHeaderController setHeaderDetailsClickListener(
@@ -232,11 +241,13 @@
     }
 
     private void bindHeaderDetailsView() {
-        CharSequence detailsText = "";
-        try {
-            detailsText = mContext.getText(mHeaderDetailsRes);
-        } catch (Resources.NotFoundException e) {
-            Log.e(TAG, "Resource of header details can't not be found!", e);
+        CharSequence detailsText = mHeaderDetails;
+        if (TextUtils.isEmpty(detailsText)) {
+            try {
+                detailsText = mContext.getText(mHeaderDetailsRes);
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Resource of header details can't not be found!", e);
+            }
         }
         mHeaderDetailsView.setText(detailsText);
         mHeaderDetailsView.setVisibility(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
index 63a958e..9a07ca8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -85,6 +85,23 @@
     }
 
     @Test
+    public void setHeaderDetails_onlyDetailsTextSet_shouldSetToDetailsView() {
+        mController.setHeaderDetails(TITLE).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.getText()).isEqualTo(TITLE);
+    }
+
+    @Test
+    public void setHeaderDetails_detailsTextAndResBothSet_shouldSetTextToDetailsView() {
+        mController.setHeaderDetailsRes(R.string.expand_button_title);
+        mController.setHeaderDetails(TITLE).apply();
+        final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+
+        assertThat(view.getText()).isEqualTo(TITLE);
+    }
+
+    @Test
     public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() {
         mController.setHeaderDetailsClickListener(v -> {
         }).apply();
diff --git a/packages/SystemUI/res/layout/bubble_permission_view.xml b/packages/SystemUI/res/layout/bubble_permission_view.xml
index 7fbb78a..c9d8a91 100644
--- a/packages/SystemUI/res/layout/bubble_permission_view.xml
+++ b/packages/SystemUI/res/layout/bubble_permission_view.xml
@@ -17,7 +17,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/bubble_permission_height"
     android:animateLayoutChanges="true"
     android:orientation="vertical"
     android:paddingStart="@dimen/bubble_expanded_header_horizontal_padding"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1f6e3c2..536bc4e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1045,4 +1045,8 @@
     <dimen name="bubble_header_icon_size">48dp</dimen>
     <!-- Size of the app icon shown in the bubble permission view -->
     <dimen name="bubble_permission_icon_size">24dp</dimen>
+    <!-- Space between the pointer triangle and the bubble expanded view -->
+    <dimen name="bubble_pointer_margin">8dp</dimen>
+    <!-- Height of the permission prompt shown with bubbles -->
+    <dimen name="bubble_permission_height">120dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 4eea9f8..471619e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -80,16 +80,21 @@
     // Enables some subset of notifs to automatically become bubbles
     private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false;
 
-    // Secure settings flags
-    // Feature level flag
+    /** Flag to enable or disable the entire feature */
     private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
-    // Auto bubble flags set whether different notification types should be presented as a bubble
+    /** Auto bubble flags set whether different notif types should be presented as a bubble */
     private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
     private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
     private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
-    // Use an activity view for an auto-bubbled notification if it has an appropriate content intent
+
+    /** Use an activityView for an auto-bubbled notifs if it has an appropriate content intent */
     private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
 
+    /** Whether the row of bubble circles are anchored to the top or bottom of the screen. */
+    private static final String ENABLE_BUBBLES_AT_TOP = "experiment_enable_top_bubbles";
+    /** Flag to position the header below the activity view */
+    private static final String ENABLE_BUBBLE_FOOTER = "experiment_enable_bubble_footer";
+
     private final Context mContext;
     private final NotificationEntryManager mNotificationEntryManager;
     private final IActivityTaskManager mActivityTaskManager;
@@ -548,6 +553,22 @@
                 ENABLE_BUBBLES, 1) != 0;
     }
 
+    /**
+     * Whether bubbles should be positioned at the top of the screen or not.
+     */
+    public static boolean showBubblesAtTop(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLES_AT_TOP, 0) != 0;
+    }
+
+    /**
+     * Whether the bubble chrome should display as a footer or not (in which case it's a header).
+     */
+    public static boolean useFooter(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_BUBBLE_FOOTER, 0) != 0;
+    }
+
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
     private class BubblesImeListener extends IPinnedStackListener.Stub {
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 7884800..b635033 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -70,8 +70,13 @@
 public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
     private static final String TAG = "BubbleExpandedView";
 
+    // Configurable via bubble settings; just for testing
+    private boolean mUseFooter;
+    private boolean mShowOnTop;
+
     // The triangle pointing to the expanded view
     private View mPointerView;
+    private int mPointerMargin;
 
     // Header
     private View mHeaderView;
@@ -90,6 +95,8 @@
 
     private int mMinHeight;
     private int mHeaderHeight;
+    private int mBubbleHeight;
+    private int mPermissionHeight;
 
     private NotificationEntry mEntry;
     private PackageManager mPm;
@@ -150,6 +157,7 @@
         mPm = context.getPackageManager();
         mMinHeight = getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_default_height);
+        mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin);
         try {
             mNotificationManagerService = INotificationManager.Stub.asInterface(
                     ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
@@ -172,8 +180,11 @@
         int bgColor = ta.getColor(0, Color.WHITE);
         ta.recycle();
 
+        mShowOnTop = BubbleController.showBubblesAtTop(getContext());
+        mUseFooter = BubbleController.useFooter(getContext());
+
         ShapeDrawable triangleDrawable = new ShapeDrawable(
-                TriangleShape.create(width, height, true /* pointUp */));
+                TriangleShape.create(width, height, mShowOnTop /* pointUp */));
         triangleDrawable.setTint(bgColor);
         mPointerView.setBackground(triangleDrawable);
 
@@ -195,6 +206,8 @@
 
         mHeaderHeight = getContext().getResources().getDimensionPixelSize(
                 R.dimen.bubble_expanded_header_height);
+        mPermissionHeight = getContext().getResources().getDimensionPixelSize(
+                R.dimen.bubble_permission_height);
         mHeaderView = findViewById(R.id.header_layout);
         mDeepLinkIcon = findViewById(R.id.deep_link_button);
         mSettingsIcon = findViewById(R.id.settings_button);
@@ -226,6 +239,15 @@
             activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
             return view.onApplyWindowInsets(insets);
         });
+
+        if (!mShowOnTop) {
+            removeView(mPointerView);
+            if (mUseFooter) {
+                removeView(viewWrapper);
+                addView(viewWrapper);
+            }
+            addView(mPointerView);
+        }
     }
 
     /**
@@ -332,7 +354,11 @@
 
             // Use notification view
             mNotifRow = mEntry.getRow();
-            addView(mNotifRow);
+            if (mShowOnTop) {
+                addView(mNotifRow);
+            } else {
+                addView(mNotifRow, mUseFooter ? 0 : 1);
+            }
         }
         updateView();
     }
@@ -345,6 +371,17 @@
         return true;
     }
 
+    /**
+     * @return total height that the expanded view occupies.
+     */
+    int getExpandedSize() {
+        int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE
+                ? mHeaderHeight
+                : mPermissionHeight;
+        return mBubbleHeight + mPointerView.getHeight() + mPointerMargin
+                + chromeHeight;
+    }
+
     void updateHeight() {
         if (usingActivityView()) {
             Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
@@ -358,12 +395,19 @@
                         ? data.getDesiredHeight()
                         : mMinHeight;
             }
-            int max = mStackView.getMaxExpandedHeight() - mHeaderHeight;
+            int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE
+                    ? mHeaderHeight
+                    : mPermissionHeight;
+            int max = mStackView.getMaxExpandedHeight() - chromeHeight - mPointerView.getHeight()
+                    - mPointerMargin;
             int height = Math.min(desiredHeight, max);
             height = Math.max(height, mMinHeight);
             LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
             lp.height = height;
+            mBubbleHeight = height;
             mActivityView.setLayoutParams(lp);
+        } else {
+            mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight;
         }
     }
 
@@ -412,6 +456,7 @@
             } else if (mOnBubbleBlockedListener != null) {
                 mOnBubbleBlockedListener.onBubbleBlocked(mEntry);
             }
+            mStackView.onExpandedHeightChanged();
             logBubbleClickEvent(mEntry.notification,
                     allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN :
                             StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e20be8e..167bf47 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -173,7 +173,7 @@
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
 
         mStackAnimationController = new StackAnimationController();
-        mExpandedAnimationController = new ExpandedAnimationController();
+        mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize);
 
         mBubbleContainer = new PhysicsAnimationLayout(context);
         mBubbleContainer.setMaxRenderedChildren(
@@ -513,8 +513,7 @@
             final float yStart = Math.min(
                     mStackAnimationController.getStackPosition().y,
                     mExpandedAnimateYDistance);
-            final float yDest = getStatusBarHeight()
-                    + mExpandedBubble.iconView.getHeight() + mBubblePadding;
+            final float yDest = getYPositionForExpandedView();
 
             if (shouldExpand) {
                 mExpandedViewContainer.setTranslationX(xStart);
@@ -668,13 +667,39 @@
      * y position when the bubbles are expanded as well as the bounds of the dismiss target.
      */
     int getMaxExpandedHeight() {
+        boolean showOnTop = BubbleController.showBubblesAtTop(getContext());
         int expandedY = (int) mExpandedAnimationController.getExpandedY();
-        int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null
-                ? mBubbleContainer.getChildAt(0).getHeight()
-                : 0;
-        // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
-        int pipDismissHeight = mPipDismissHeight - getBottomInset();
-        return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
+        if (showOnTop) {
+            // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset
+            int pipDismissHeight = mPipDismissHeight - getBottomInset();
+            return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight;
+        } else {
+            return expandedY - getStatusBarHeight();
+        }
+    }
+
+    /**
+     * Calculates the y position of the expanded view when it is expanded.
+     */
+    float getYPositionForExpandedView() {
+        boolean showOnTop = BubbleController.showBubblesAtTop(getContext());
+        if (showOnTop) {
+            return getStatusBarHeight() + mBubbleSize + mBubblePadding;
+        } else {
+            return mExpandedAnimationController.getExpandedY()
+                    - mExpandedBubble.expandedView.getExpandedSize() - mBubblePadding;
+        }
+    }
+
+    /**
+     * Called when the height of the currently expanded view has changed (not via an
+     * update to the bubble's desired height but for some other reason, e.g. permission view
+     * goes away).
+     */
+    void onExpandedHeightChanged() {
+        if (mIsExpanded) {
+            requestUpdate();
+        }
     }
 
     /**
@@ -751,6 +776,8 @@
 
         mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         if (mIsExpanded) {
+            final float y = getYPositionForExpandedView();
+            mExpandedViewContainer.setTranslationY(y);
             mExpandedBubble.expandedView.updateView();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index f0d9be1..f7896b0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.bubbles.animation;
 
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.view.View;
 import android.view.WindowInsets;
@@ -25,6 +26,7 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController;
 
 import com.google.android.collect.Sets;
 
@@ -61,6 +63,14 @@
     private float mBubbleSizePx;
     /** Height of the status bar. */
     private float mStatusBarHeight;
+    /** Size of display. */
+    private Point mDisplaySize;
+    /** Size of dismiss target at bottom of screen. */
+    private float mPipDismissHeight;
+
+    public ExpandedAnimationController(Point displaySize) {
+        mDisplaySize = displaySize;
+    }
 
     /**
      * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
@@ -88,6 +98,7 @@
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
         mStatusBarHeight =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+        mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height);
     }
 
     /**
@@ -204,16 +215,19 @@
 
     /** The Y value of the row of expanded bubbles. */
     public float getExpandedY() {
+        boolean showOnTop = mLayout != null
+                && BubbleController.showBubblesAtTop(mLayout.getContext());
         final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
-        if (insets != null) {
+        if (showOnTop && insets != null) {
             return mBubblePaddingPx + Math.max(
                     mStatusBarHeight,
                     insets.getDisplayCutout() != null
                             ? insets.getDisplayCutout().getSafeInsetTop()
                             : 0);
+        } else {
+            int bottomInset = insets != null ? insets.getSystemWindowInsetBottom() : 0;
+            return mDisplaySize.y - mBubbleSizePx - (mPipDismissHeight - bottomInset);
         }
-
-        return mBubblePaddingPx;
     }
 
     /** Runs the given Runnable after all translation-related animations have ended. */
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 7312cbc..e27c25e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -27,7 +27,6 @@
 import android.database.ContentObserver;
 import android.os.BatteryManager;
 import android.os.Handler;
-import android.os.HardwarePropertiesManager;
 import android.os.IBinder;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
@@ -70,7 +69,6 @@
     final Receiver mReceiver = new Receiver();
 
     private PowerManager mPowerManager;
-    private HardwarePropertiesManager mHardwarePropertiesManager;
     private WarningsUI mWarnings;
     private final Configuration mLastConfiguration = new Configuration();
     private long mTimeRemaining = Long.MAX_VALUE;
@@ -96,8 +94,6 @@
 
     public void start() {
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mHardwarePropertiesManager = (HardwarePropertiesManager)
-                mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
         mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
         mWarnings = Dependency.get(WarningsUI.class);
         mEnhancedEstimates = Dependency.get(EnhancedEstimates.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index 3bd582f..b4059c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -40,7 +41,8 @@
 public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase {
 
     @Spy
-    private ExpandedAnimationController mExpandedController = new ExpandedAnimationController();
+    private ExpandedAnimationController mExpandedController =
+            new ExpandedAnimationController(new Point(500, 1000) /* displaySize */);
 
     private int mStackOffset;
     private float mBubblePadding;
@@ -167,7 +169,7 @@
             assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
                     mLayout.getChildAt(i).getTranslationX(),
                     2f);
-            assertEquals(mBubblePadding + mCutoutInsetSize,
+            assertEquals(mExpandedController.getExpandedY(),
                     mLayout.getChildAt(i).getTranslationY(), 2f);
 
             if (i < mMaxRenderedBubbles) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4a4e247..0aed63d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -31,7 +31,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
-import android.os.HardwarePropertiesManager;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
 import android.os.PowerManager;
@@ -76,7 +75,6 @@
     private static final int OLD_BATTERY_LEVEL_NINE = 9;
     private static final int OLD_BATTERY_LEVEL_10 = 10;
     private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
-    private HardwarePropertiesManager mHardProps;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
     private EnhancedEstimates mEnhancedEstimates;
@@ -90,10 +88,8 @@
         MockitoAnnotations.initMocks(this);
         mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
         mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
-        mHardProps = mock(HardwarePropertiesManager.class);
 
         mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps);
         mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
 
         createPowerUi();
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 33555c4..b3e5f69 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3595,7 +3595,7 @@
     // OPEN: Settings > Apps > Default Apps > Default sms
     DEFAULT_SMS_PICKER = 789;
 
-    // OPEN: Settings > Apps > Default Apps > Default notification assistant
+    // OPEN: Settings > Apps > Notification > Notification Assistant
     DEFAULT_NOTIFICATION_ASSISTANT = 790;
 
     // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection
diff --git a/services/art-profile b/services/art-profile
index e750380..7892fcb 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2942,7 +2942,7 @@
 HSPLcom/android/server/policy/WindowManagerPolicy;->onKeyguardOccludedChangedLw(Z)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->onLockTaskStateChangedLw(I)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->onSystemUiStarted()V
-HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z
+HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedback(ILjava/lang/String;IZ)Z
 HSPLcom/android/server/policy/WindowManagerPolicy;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I
 HSPLcom/android/server/policy/WindowManagerPolicy;->registerShortcutKey(JLcom/android/internal/policy/IShortcutService;)V
 HSPLcom/android/server/policy/WindowManagerPolicy;->removeWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;)V
@@ -15736,7 +15736,7 @@
 PLcom/android/server/policy/PhoneWindowManager;->onKeyguardOccludedChangedLw(Z)V
 PLcom/android/server/policy/PhoneWindowManager;->onOverlayChangedLw()V
 PLcom/android/server/policy/PhoneWindowManager;->onSystemUiStarted()V
-PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z
+PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedback(ILjava/lang/String;IZ)Z
 PLcom/android/server/policy/PhoneWindowManager;->powerPress(JZI)V
 PLcom/android/server/policy/PhoneWindowManager;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I
 PLcom/android/server/policy/PhoneWindowManager;->readCameraLensCoverState()V
@@ -18331,7 +18331,7 @@
 PLcom/android/server/wm/Session;->killSessionLocked()V
 PLcom/android/server/wm/Session;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
 PLcom/android/server/wm/Session;->onWindowSurfaceVisibilityChanged(Lcom/android/server/wm/WindowSurfaceController;ZI)V
-PLcom/android/server/wm/Session;->performHapticFeedback(Landroid/view/IWindow;IZ)Z
+PLcom/android/server/wm/Session;->performHapticFeedback(IZ)Z
 PLcom/android/server/wm/Session;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 PLcom/android/server/wm/Session;->remove(Landroid/view/IWindow;)V
 PLcom/android/server/wm/Session;->sendWallpaperCommand(Landroid/os/IBinder;Ljava/lang/String;IIILandroid/os/Bundle;Z)Landroid/os/Bundle;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 364e537..2e99654 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1085,6 +1085,7 @@
                             if (remoteService != null) {
                                 remoteService.destroy();
                             }
+                            mRemoteAugmentedAutofillService = null;
                         }
                     }, mMaster.isInstantServiceAllowed(), mMaster.verbose);
         }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index de9896e..f9fda64 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -26,7 +26,6 @@
 import android.service.contentcapture.IContentCaptureService;
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.SnapshotData;
-import android.text.format.DateUtils;
 import android.util.Slog;
 import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.UserDataRemovalRequest;
@@ -38,8 +37,6 @@
         extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
         IContentCaptureService> {
 
-    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
-
     private final IBinder mServerCallback;
 
     RemoteContentCaptureService(Context context, String serviceInterface,
@@ -65,12 +62,6 @@
         return PERMANENT_BOUND_TIMEOUT_MS;
     }
 
-    @Override // from AbstractRemoteService
-    protected long getRemoteRequestMillis() {
-        // TODO(b/111276913): read from Settings so it can be changed in the field
-        return TIMEOUT_REMOTE_REQUEST_MILLIS;
-    }
-
     @Override // from RemoteService
     protected void handleOnConnectedStateChanged(boolean state) {
         if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) {
diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java
index 6899c3f..647727f 100644
--- a/services/core/java/com/android/server/gpu/GpuService.java
+++ b/services/core/java/com/android/server/gpu/GpuService.java
@@ -62,6 +62,7 @@
 
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
     private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt";
+    private static final String GAME_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
     private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP;
 
     private final Context mContext;
@@ -162,6 +163,25 @@
         }
     }
 
+    private static void assetToSettingsGlobal(Context context, Context driverContext,
+            String fileName, String settingsGlobal, CharSequence delimiter) {
+        try {
+            final BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(driverContext.getAssets().open(fileName)));
+            final ArrayList<String> assetStrings = new ArrayList<>();
+            for (String assetString; (assetString = reader.readLine()) != null; ) {
+                assetStrings.add(assetString);
+            }
+            Settings.Global.putString(context.getContentResolver(),
+                                      settingsGlobal,
+                                      String.join(delimiter, assetStrings));
+        } catch (IOException e) {
+            if (DEBUG) {
+                Slog.w(TAG, "Failed to load " + fileName + ", abort.");
+            }
+        }
+    }
+
     private void fetchGameDriverPackageProperties() {
         final ApplicationInfo driverInfo;
         try {
@@ -186,29 +206,25 @@
         // Reset the whitelist.
         Settings.Global.putString(mContentResolver,
                                   Settings.Global.GAME_DRIVER_WHITELIST, "");
+        // Reset the sphal libraries
+        Settings.Global.putString(mContentResolver,
+                                  Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, "");
         mGameDriverVersionCode = driverInfo.longVersionCode;
 
         try {
             final Context driverContext = mContext.createPackageContext(mDriverPackageName,
                                                                         Context.CONTEXT_RESTRICTED);
-            final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(driverContext.getAssets()
-                                              .open(GAME_DRIVER_WHITELIST_FILENAME)));
-            final ArrayList<String> whitelistedPackageNames = new ArrayList<>();
-            for (String packageName; (packageName = reader.readLine()) != null; ) {
-                whitelistedPackageNames.add(packageName);
-            }
-            Settings.Global.putString(mContentResolver,
-                                      Settings.Global.GAME_DRIVER_WHITELIST,
-                                      String.join(",", whitelistedPackageNames));
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME,
+                    Settings.Global.GAME_DRIVER_WHITELIST, ",");
+
+            assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_SPHAL_LIBRARIES_FILENAME,
+                    Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, ":");
+
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
                 Slog.w(TAG, "driver package '" + mDriverPackageName + "' not installed");
             }
-        } catch (IOException e) {
-            if (DEBUG) {
-                Slog.w(TAG, "Failed to load whitelist driver package, abort.");
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3557fcf..e8c402c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -594,8 +594,6 @@
             mConditionProviders.migrateToXml();
             handleSavePolicyFile();
         }
-
-        mAssistants.ensureAssistant();
     }
 
     private void loadPolicyFile() {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bd74174..d501c4f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -822,6 +822,13 @@
     }
 
     @Override
+    public void installExistingPackage(String packageName, int installFlags, int installReason,
+            IntentSender statusReceiver, int userId) {
+        mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason,
+                statusReceiver);
+    }
+
+    @Override
     public void setPermissionsResult(int sessionId, boolean accepted) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 56ef33a..be2b985 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1491,12 +1491,12 @@
             // Inherit base if not overridden
             if (mResolvedBaseFile == null) {
                 mResolvedBaseFile = new File(appInfo.getBaseCodePath());
-                mResolvedInheritedFiles.add(mResolvedBaseFile);
+                resolveInheritedFile(mResolvedBaseFile);
                 // Inherit the dex metadata if present.
                 final File baseDexMetadataFile =
                         DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile);
                 if (baseDexMetadataFile != null) {
-                    mResolvedInheritedFiles.add(baseDexMetadataFile);
+                    resolveInheritedFile(baseDexMetadataFile);
                 }
                 baseApk = existingBase;
             }
@@ -1508,12 +1508,12 @@
                     final File splitFile = new File(existing.splitCodePaths[i]);
                     final boolean splitRemoved = removeSplitList.contains(splitName);
                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
-                        mResolvedInheritedFiles.add(splitFile);
+                        resolveInheritedFile(splitFile);
                         // Inherit the dex metadata if present.
                         final File splitDexMetadataFile =
                                 DexMetadataHelper.findDexMetadataForFile(splitFile);
                         if (splitDexMetadataFile != null) {
-                            mResolvedInheritedFiles.add(splitDexMetadataFile);
+                            resolveInheritedFile(splitDexMetadataFile);
                         }
                     }
                 }
@@ -1627,6 +1627,17 @@
         mResolvedStagedFiles.add(stagedSignature);
     }
 
+    private void resolveInheritedFile(File origFile) {
+        mResolvedInheritedFiles.add(origFile);
+
+        // Inherit the fsverity signature file if present.
+        final File fsveritySignatureFile = new File(
+                VerityUtils.getFsveritySignatureFilePath(origFile.getPath()));
+        if (fsveritySignatureFile.exists()) {
+            mResolvedInheritedFiles.add(fsveritySignatureFile);
+        }
+    }
+
     @GuardedBy("mLock")
     private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4bca5e0..00b02ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1302,12 +1302,14 @@
     // Recordkeeping of restore-after-install operations that are currently in flight
     // between the Package Manager and the Backup Manager
     static class PostInstallData {
-        public InstallArgs args;
-        public PackageInstalledInfo res;
+        public final InstallArgs args;
+        public final PackageInstalledInfo res;
+        public final Runnable mPostInstallRunnable;
 
-        PostInstallData(InstallArgs _a, PackageInstalledInfo _r) {
+        PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) {
             args = _a;
             res = _r;
+            mPostInstallRunnable = postInstallRunnable;
         }
     }
 
@@ -1439,7 +1441,9 @@
                     final boolean didRestore = (msg.arg2 != 0);
                     mRunningInstalls.delete(msg.arg1);
 
-                    if (data != null) {
+                    if (data != null && data.mPostInstallRunnable != null) {
+                        data.mPostInstallRunnable.run();
+                    } else if (data != null) {
                         InstallArgs args = data.args;
                         PackageInstalledInfo parentRes = data.res;
 
@@ -12766,6 +12770,11 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
+        return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null);
+    }
+
+    int installExistingPackageAsUser(String packageName, int userId, int installFlags,
+            int installReason, IntentSender intentSender) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
                     + " installFlags=" + installFlags + " installReason=" + installReason);
@@ -12845,7 +12854,11 @@
                 PackageInstalledInfo res =
                         createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
                 res.pkg = pkgSetting.pkg;
-                restoreAndPostInstall(userId, res, null);
+                res.newUsers = new int[]{ userId };
+                PostInstallData postInstallData = intentSender == null ? null :
+                        new PostInstallData(null, res, () -> onRestoreComplete(res.returnCode,
+                              mContext, intentSender));
+                restoreAndPostInstall(userId, res, postInstallData);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -12854,6 +12867,16 @@
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
+    static void onRestoreComplete(int returnCode, Context context, IntentSender target) {
+        Intent fillIn = new Intent();
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                PackageManager.installStatusToPublicStatus(returnCode));
+        try {
+            target.sendIntent(context, 0, fillIn, null, null);
+        } catch (SendIntentException ignored) {
+        }
+    }
+
     static void setInstantAppForUser(PackageSetting pkgSetting, int userId,
             boolean instantApp, boolean fullApp) {
         // no state specified; do nothing
@@ -13816,7 +13839,7 @@
             }
             for (InstallRequest request : installRequests) {
                 restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
-                        new PostInstallData(request.args, request.installResult));
+                        new PostInstallData(request.args, request.installResult, null));
             }
         });
     }
@@ -17130,7 +17153,7 @@
 
             if (!legacyMode) {
                 // fs-verity is optional for now.  Only set up if signature is provided.
-                if (new File(signaturePath).exists()) {
+                if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
                     try {
                         VerityUtils.setUpFsverity(filePath, signaturePath);
                     } catch (IOException | DigestException | NoSuchAlgorithmException
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2eb762b..5216967 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1114,6 +1114,7 @@
         int userId = UserHandle.USER_SYSTEM;
         int installFlags = 0;
         String opt;
+        boolean waitTillComplete = false;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
                 case "--user":
@@ -1128,6 +1129,9 @@
                     installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
                     installFlags |= PackageManager.INSTALL_FULL_APP;
                     break;
+                case "--wait":
+                    waitTillComplete = true;
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -1140,9 +1144,23 @@
             return 1;
         }
 
+        int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
         try {
+            if (waitTillComplete) {
+                final LocalIntentReceiver receiver = new LocalIntentReceiver();
+                final IPackageInstaller installer = mInterface.getPackageInstaller();
+                pw.println("Installing package " + packageName + " for user: " + userId);
+                installer.installExistingPackage(packageName, installFlags, installReason,
+                        receiver.getIntentSender(), userId);
+                final Intent result = receiver.getResult();
+                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                        PackageInstaller.STATUS_FAILURE);
+                pw.println("Received intent for package install");
+                return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1;
+            }
+
             final int res = mInterface.installExistingPackageAsUser(packageName, userId,
-                    installFlags, PackageManager.INSTALL_REASON_UNKNOWN);
+                    installFlags, installReason);
             if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
                 throw new NameNotFoundException("Package " + packageName + " doesn't exist");
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 68dab34..0a5dc2e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -808,7 +808,7 @@
         public void onWakeUp() {
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
-                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                             "Wake Up");
                     wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
                             PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
@@ -1212,21 +1212,21 @@
             break;
         case LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Global Actions");
             showGlobalActionsInternal();
             break;
         case LONG_PRESS_POWER_SHUT_OFF:
         case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Shut Off");
             sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
             mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
             break;
         case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Long Press - Go To Voice Assist");
             final boolean keyguardActive = mKeyguardDelegate == null
                     ? false
@@ -1249,7 +1249,7 @@
             break;
         case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
             mPowerKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Power - Very Long Press - Show Global Actions");
             showGlobalActionsInternal();
             break;
@@ -1400,7 +1400,7 @@
         @Override
         public void run() {
             mEndCallKeyHandled = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "End Call - Long Press - Show Global Actions");
             showGlobalActionsInternal();
         }
@@ -1667,7 +1667,7 @@
                 return;
             }
             mHomeConsumed = true;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                     "Home - Long Press");
             switch (mLongPressOnHomeBehavior) {
                 case LONG_PRESS_HOME_ALL_APPS:
@@ -3277,7 +3277,7 @@
     }
 
     private void launchAssistLongPressAction() {
-        performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false,
+        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                 "Assist - Long Press");
         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
 
@@ -4039,7 +4039,7 @@
         }
 
         if (useHapticFeedback) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false,
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                     "Virtual Key - Press");
         }
 
@@ -4760,7 +4760,7 @@
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
         if (safeMode) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
+            performHapticFeedback(HapticFeedbackConstants.SAFE_MODE_ENABLED, true,
                     "Safe Mode Enabled");
         }
     }
@@ -5237,9 +5237,14 @@
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
+    private boolean performHapticFeedback(int effectId, boolean always, String reason) {
+        return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(),
+            effectId, always, reason);
+    }
+
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason) {
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason) {
         if (!mVibrator.hasVibrator()) {
             return false;
         }
@@ -5254,16 +5259,7 @@
             return false;
         }
 
-        int owningUid;
-        String owningPackage;
-        if (win != null) {
-            owningUid = win.getOwningUid();
-            owningPackage = win.getOwningPackage();
-        } else {
-            owningUid = android.os.Process.myUid();
-            owningPackage = mContext.getOpPackageName();
-        }
-        mVibrator.vibrate(owningUid, owningPackage, effect, reason, VIBRATION_ATTRIBUTES);
+        mVibrator.vibrate(uid, packageName, effect, reason, VIBRATION_ATTRIBUTES);
         return true;
     }
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index d7e4b6c..c741513 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1300,8 +1300,8 @@
     /**
      * Call from application to perform haptic feedback on its window.
      */
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason);
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason);
 
     /**
      * Called when we have started keeping the screen on because a window
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c6b7060..34a4802 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -236,16 +236,13 @@
         }
         mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false);
 
-        // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
-        // http://developer.android.com/guide/practices/screens_support.html#range
-        // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
-        // so if the orientation is forced, we need to respect that no matter what.
+        // It's physically impossible to rotate the car's screen.
         final boolean isCar = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_AUTOMOTIVE);
-        // For TV, it's usually 960dp x 540dp, ignore the size limitation.
-        // so if the orientation is forced, we need to respect that no matter what.
+        // It's also not likely to rotate a TV screen.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
+        // Not much of use to rotate the display since it's close to square.
         final boolean isCloseToSquare =
                 isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height);
         final boolean forceDesktopMode =
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 58cf73a..dc8c7b7 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -245,17 +245,13 @@
     }
 
     @Override
-    public boolean performHapticFeedback(IWindow window, int effectId,
-            boolean always) {
-        synchronized (mService.mGlobalLock) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                return mService.mPolicy.performHapticFeedbackLw(
-                        mService.windowForClientLocked(this, window, true),
+    public boolean performHapticFeedback(int effectId, boolean always) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
                         effectId, always, null);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cc62138..31788ae 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -99,7 +99,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
-import android.provider.Settings.Secure;
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
@@ -2519,7 +2518,7 @@
         verify(mListeners, times(1)).migrateToXml();
         verify(mConditionProviders, times(1)).migrateToXml();
         verify(mAssistants, times(1)).migrateToXml();
-        verify(mAssistants, times(2)).ensureAssistant();
+        verify(mAssistants, never()).ensureAssistant();
     }
 
     @Test
@@ -2539,7 +2538,7 @@
         verify(mListeners, times(2)).migrateToXml();
         verify(mConditionProviders, times(2)).migrateToXml();
         verify(mAssistants, times(2)).migrateToXml();
-        verify(mAssistants, times(2)).ensureAssistant();
+        verify(mAssistants, never()).ensureAssistant();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 849772a..c3561f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -313,8 +313,8 @@
     }
 
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
-            String reason) {
+    public boolean performHapticFeedback(int uid, String packageName, int effectId,
+            boolean always, String reason) {
         return false;
     }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 22dc2ed..ab7bb68 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -17,14 +17,13 @@
 package android.net.wifi.p2p;
 
 import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
 
 import java.util.Objects;
-
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * A class representing a Wi-Fi p2p device
@@ -360,7 +359,9 @@
             deviceCapability = source.deviceCapability;
             groupCapability = source.groupCapability;
             status = source.status;
-            wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
+            if (source.wfdInfo != null) {
+                wfdInfo = new WifiP2pWfdInfo(source.wfdInfo);
+            }
         }
     }
 
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index f61e6b7..17ee755 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -30,6 +30,31 @@
 public class WifiP2pDeviceTest {
 
     /**
+     * Compare two p2p devices.
+     *
+     * @param devA is the first device to be compared
+     * @param devB is the second device to be compared
+     */
+    private void compareWifiP2pDevices(WifiP2pDevice devA, WifiP2pDevice devB) {
+        assertEquals(devA.deviceName, devB.deviceName);
+        assertEquals(devA.deviceAddress, devB.deviceAddress);
+        assertEquals(devA.primaryDeviceType, devB.primaryDeviceType);
+        assertEquals(devA.secondaryDeviceType, devB.secondaryDeviceType);
+        assertEquals(devA.wpsConfigMethodsSupported, devB.wpsConfigMethodsSupported);
+        assertEquals(devA.deviceCapability, devB.deviceCapability);
+        assertEquals(devA.groupCapability, devB.groupCapability);
+        assertEquals(devA.status, devB.status);
+        if (devA.wfdInfo != null) {
+            assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled());
+            assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex());
+            assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort());
+            assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput());
+        } else {
+            assertEquals(devA.wfdInfo, devB.wfdInfo);
+        }
+    }
+
+    /**
      * Check equals and hashCode consistency
      */
     @Test
@@ -42,4 +67,52 @@
         assertTrue(dev_a.equals(dev_b));
         assertEquals(dev_a.hashCode(), dev_b.hashCode());
     }
+
+    /**
+     * Check the copy constructor with default values.
+     */
+    @Test
+    public void testCopyConstructorWithDefaultValues() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
+
+    /**
+     * Check the copy constructor with updated values.
+     */
+    @Test
+    public void testCopyConstructorWithUpdatedValues() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        device.deviceName = "deviceName";
+        device.deviceAddress = "11:22:33:44:55:66";
+        device.primaryDeviceType = "primaryDeviceType";
+        device.secondaryDeviceType = "secondaryDeviceType";
+        device.wpsConfigMethodsSupported = 0x0008;
+        device.deviceCapability = 1;
+        device.groupCapability = 1;
+        device.status = WifiP2pDevice.CONNECTED;
+        device.wfdInfo = new WifiP2pWfdInfo();
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
+
+    /**
+     * Check the copy constructor when the wfdInfo of the source object is null.
+     */
+    @Test
+    public void testCopyConstructorWithNullWfdInfo() throws Exception {
+        WifiP2pDevice device = new WifiP2pDevice();
+        device.deviceName = "deviceName";
+        device.deviceAddress = "11:22:33:44:55:66";
+        device.primaryDeviceType = "primaryDeviceType";
+        device.secondaryDeviceType = "secondaryDeviceType";
+        device.wpsConfigMethodsSupported = 0x0008;
+        device.deviceCapability = 1;
+        device.groupCapability = 1;
+        device.status = WifiP2pDevice.CONNECTED;
+        device.wfdInfo = null;
+        WifiP2pDevice copy = new WifiP2pDevice(device);
+        compareWifiP2pDevices(device, copy);
+    }
 }