Merge "Fix NullPointerException in PIA when installing from Files app" into udc-dev
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index de66f05..56f6f82 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2072,6 +2072,25 @@
         return new InstallInfo(result);
     }
 
+    /**
+     * Parse a single APK file passed as an FD to get install relevant information about
+     * the package wrapped in {@link InstallInfo}.
+     * @throws PackageParsingException if the package source file(s) provided is(are) not valid,
+     * or the parser isn't able to parse the supplied source(s).
+     * @hide
+     */
+    @NonNull
+    public InstallInfo readInstallInfo(@NonNull ParcelFileDescriptor pfd,
+            @Nullable String debugPathName, int flags) throws PackageParsingException {
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<PackageLite> result = ApkLiteParseUtils.parseMonolithicPackageLite(input,
+                pfd.getFileDescriptor(), debugPathName, flags);
+        if (result.isError()) {
+            throw new PackageParsingException(result.getErrorCode(), result.getErrorMessage());
+        }
+        return new InstallInfo(result);
+    }
+
     // (b/239722738) This class serves as a bridge between the PackageLite class, which
     // is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java)
     // This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or
@@ -2125,6 +2144,21 @@
         public long calculateInstalledSize(@NonNull SessionParams params) throws IOException {
             return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride);
         }
+
+        /**
+         * @param params {@link SessionParams} of the installation
+         * @param pfd of an APK opened for read
+         * @return Total disk space occupied by an application after installation.
+         * Includes the size of the raw APKs, possibly unpacked resources, raw dex metadata files,
+         * and all relevant native code.
+         * @throws IOException when size of native binaries cannot be calculated.
+         * @hide
+         */
+        public long calculateInstalledSize(@NonNull SessionParams params,
+                @NonNull ParcelFileDescriptor pfd) throws IOException {
+            return InstallLocationUtils.calculateInstalledSize(mPkg, params.abiOverride,
+                    pfd.getFileDescriptor());
+        }
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index a4339d4..d209b35 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -109,7 +109,7 @@
     }
 
     /**
-     * Parse lightweight details about a single APK files.
+     * Parse lightweight details about a single APK file.
      */
     public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
             File packageFile, int flags) {
@@ -135,6 +135,33 @@
     }
 
     /**
+     * Parse lightweight details about a single APK file passed as an FD.
+     */
+    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
+            FileDescriptor packageFd, String debugPathName, int flags) {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        try {
+            final ParseResult<ApkLite> result = parseApkLite(input, packageFd, debugPathName,
+                    flags);
+            if (result.isError()) {
+                return input.error(result);
+            }
+
+            final ApkLite baseApk = result.getResult();
+            final String packagePath = debugPathName;
+            return input.success(
+                    new PackageLite(packagePath, baseApk.getPath(), baseApk, null /* splitNames */,
+                            null /* isFeatureSplits */, null /* usesSplitNames */,
+                            null /* configForSplit */, null /* splitApkPaths */,
+                            null /* splitRevisionCodes */, baseApk.getTargetSdkVersion(),
+                            null /* requiredSplitTypes */, null, /* splitTypes */
+                            baseApk.isAllowUpdateOwnership()));
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    /**
      * Parse lightweight details about a directory of APKs.
      *
      * @param packageDir is the folder that contains split apks for a regular app
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 628fc31..c0370cc 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityOptions.ANIM_FROM_STYLE;
 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
@@ -1067,6 +1068,11 @@
             return options;
         }
 
+        public static AnimationOptions makeSceneTransitionAnimOptions() {
+            AnimationOptions options = new AnimationOptions(ANIM_SCENE_TRANSITION);
+            return options;
+        }
+
         public int getType() {
             return mType;
         }
diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java
index 70311fd..b1aae7f 100644
--- a/graphics/java/android/graphics/MeshSpecification.java
+++ b/graphics/java/android/graphics/MeshSpecification.java
@@ -28,11 +28,40 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Class responsible for holding specifications for {@link Mesh} creations. This class
- * generates a {@link MeshSpecification} via the Make method, where multiple parameters to set up
- * the mesh are supplied, including attributes, vertex stride, varyings, and
- * vertex/fragment shaders. There are also additional methods to provide an optional
- * {@link ColorSpace} as well as an alpha type.
+ * Class responsible for holding specifications for {@link Mesh} creations. This class generates a
+ * {@link MeshSpecification} via the
+ * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String)} method,
+ * where multiple parameters to set up the mesh are supplied, including attributes, vertex stride,
+ * {@link Varying}, and vertex/fragment shaders. There are also additional methods to provide an
+ * optional {@link ColorSpace} as well as an alpha type.
+ *
+ * For example a vertex shader that leverages a {@link Varying} may look like the following:
+ *
+ * <pre>
+ *        Varyings main(const Attributes attributes) {
+ *             Varyings varyings;
+ *             varyings.position = attributes.position;
+ *             return varyings;
+ *        }
+ * </pre>
+ *
+ * The corresponding fragment shader that may consume the varying look like the following:
+ *
+ * <pre>
+ *      float2 main(const Varyings varyings, out float4 color) {
+ *             color = vec4(1.0, 0.0, 0.0, 1.0);
+ *             return varyings.position;
+ *      }
+ * </pre>
+ *
+ * The color returned from this fragment shader is blended with the other parameters that are
+ * configured on the Paint object (ex. {@link Paint#setBlendMode(BlendMode)} used to draw the mesh.
+ *
+ * The position returned in the fragment shader can be consumed by any following fragment shaders in
+ * the shader chain.
+ *
+ * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
+ * regarding Android Graphics Shader Language.
  *
  * Note that there are several limitations on various mesh specifications:
  * 1. The max amount of attributes allowed is 8.
@@ -118,7 +147,11 @@
     public static final int TYPE_UBYTE4 = 4;
 
     /**
-     * Data class to represent a single attribute in a shader.
+     * Data class to represent a single attribute in a shader. An attribute is a variable that
+     * accompanies a vertex, this can be a color or texture coordinates.
+     *
+     * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
+     * regarding Android Graphics Shader Language.
      *
      * Note that offset is the offset in number of bytes. For example, if we had two attributes
      *
@@ -128,6 +161,10 @@
      * </pre>
      *
      * att1 would have an offset of 0, while att2 would have an offset of 12 bytes.
+     *
+     * This is consumed as part of
+     * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
+     * to create a {@link MeshSpecification} instance.
      */
     public static class Attribute {
         @Type
@@ -175,7 +212,15 @@
     }
 
     /**
-     * Data class to represent a single varying variable.
+     * Data class to represent a single varying variable. A Varying variable can be altered by the
+     * vertex shader defined on the mesh but not by the fragment shader defined by AGSL.
+     *
+     * See https://developer.android.com/develop/ui/views/graphics/agsl for more information
+     * regarding Android Graphics Shader Language.
+     *
+     * This is consumed as part of
+     * {@link MeshSpecification#make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
+     * to create a {@link MeshSpecification} instance.
      */
     public static class Varying {
         @Type
@@ -220,7 +265,7 @@
 
     /**
      * Creates a {@link MeshSpecification} object for use within {@link Mesh}. This uses a default
-     * color space of {@link ColorSpace.Named#SRGB} and {@link AlphaType} of
+     * color space of {@link ColorSpace.Named#SRGB} and alphaType of
      * {@link #ALPHA_TYPE_PREMULTIPLIED}.
      *
      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
@@ -233,7 +278,11 @@
      *                       the 6 varyings allowed.
      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
      *                       varying is set within the shader to get proper results.
+     *                       See {@link MeshSpecification} for an example vertex shader
+     *                       implementation
      * @param fragmentShader fragment shader to be supplied to the mesh.
+     *                       See {@link MeshSpecification} for an example fragment shader
+     *                       implementation
      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
      */
     @NonNull
@@ -253,7 +302,7 @@
     }
 
     /**
-     * Creates a {@link MeshSpecification} object.  This uses a default {@link AlphaType} of
+     * Creates a {@link MeshSpecification} object.  This uses a default alphaType of
      * {@link #ALPHA_TYPE_PREMULTIPLIED}.
      *
      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
@@ -266,7 +315,11 @@
      *                       the 6 varyings allowed.
      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
      *                       varying is set within the shader to get proper results.
+     *                       See {@link MeshSpecification} for an example vertex shader
+     *                       implementation
      * @param fragmentShader fragment shader to be supplied to the mesh.
+     *                       See {@link MeshSpecification} for an example fragment shader
+     *                       implementation
      * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
      */
@@ -301,7 +354,11 @@
      *                       the 6 varyings allowed.
      * @param vertexShader   vertex shader to be supplied to the mesh. Ensure that the position
      *                       varying is set within the shader to get proper results.
+     *                       See {@link MeshSpecification} for an example vertex shader
+     *                       implementation
      * @param fragmentShader fragment shader to be supplied to the mesh.
+     *                       See {@link MeshSpecification} for an example fragment shader
+     *                       implementation
      * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
      * @param alphaType      Describes how to interpret the alpha component for a pixel. Must be
      *                       one of
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 6e9ecda..1ee52ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -21,6 +21,7 @@
 import static android.app.ActivityOptions.ANIM_NONE;
 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
 import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -625,6 +626,9 @@
         } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
             // This received a transferred starting window, so don't animate
             return null;
+        } else if (overrideType == ANIM_SCENE_TRANSITION) {
+            // If there's a scene-transition, then jump-cut.
+            return null;
         } else {
             a = loadAttributeAnimation(info, change, wallpaperTransit, mTransitionAnimation);
         }
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 3402857..7d71bd5 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -78,8 +78,12 @@
 
   <!-- This text is followed by a list of one or more options. [CHAR LIMIT=200] -->
   <string name="save_credential_to_title">Save <xliff:g id="credentialTypes" example="passkey">%1$s</xliff:g> to</string>
-  <!-- This appears as the title of the modal bottom sheet for users to choose to create a passkey on another device. [CHAR LIMIT=200] -->
-  <string name="create_passkey_in_other_device_title">Create passkey in another device?</string>
+  <!-- This appears as the title of the modal bottom sheet for users to confirm to create a passkey on another device. [CHAR LIMIT=200] -->
+  <string name="create_passkey_in_other_device_title">Create passkey on another device?</string>
+  <!-- This appears as the title of the modal bottom sheet for users to confirm to save a password on another device. [CHAR LIMIT=200] -->
+  <string name="save_password_on_other_device_title">Save password on another device?</string>
+  <!-- This appears as the title of the modal bottom sheet for users to confirm to save a sign-in credential on another device. [CHAR LIMIT=200] -->
+  <string name="save_sign_in_on_other_device_title">Save sign-in on another device?</string>
   <!-- This appears as the title of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=200] -->
   <string name="use_provider_for_all_title">Use <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g> for all your sign-ins?</string>
   <!-- This appears as the description body of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=300] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 6549b2d..1fb5e3f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -50,6 +50,8 @@
         super.onCreate(savedInstanceState)
         overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN,
             0, 0)
+        overrideActivityTransition(Activity.OVERRIDE_TRANSITION_CLOSE,
+            0, 0)
         Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity")
         try {
             val (isCancellationRequest, shouldShowCancellationUi, _) =
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index dfa517b..d16120f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -465,7 +465,17 @@
     SheetContainerCard {
         item { HeadlineIcon(imageVector = Icons.Outlined.QrCodeScanner) }
         item { Divider(thickness = 16.dp, color = Color.Transparent) }
-        item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) }
+        item {
+            HeadlineText(
+                text = stringResource(
+                    when (requestDisplayInfo.type) {
+                        CredentialType.PASSKEY -> R.string.create_passkey_in_other_device_title
+                        CredentialType.PASSWORD -> R.string.save_password_on_other_device_title
+                        else -> R.string.save_sign_in_on_other_device_title
+                    }
+                )
+            )
+        }
         item { Divider(thickness = 24.dp, color = Color.Transparent) }
         item {
             CredentialContainerCard {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
index 4c5875b..7b17cbd 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -16,14 +16,14 @@
 
 package com.android.packageinstaller;
 
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
 
-import java.io.File;
-
 /**
  * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
  */
@@ -52,8 +52,13 @@
         super.onDestroy();
 
         if (isFinishing()) {
-            File sourceFile = new File(getIntent().getData().getPath());
-            new Thread(sourceFile::delete).start();
+            // While we expect PIA/InstallStaging to abandon/commit the session, still there
+            // might be cases when the session becomes orphan.
+            int sessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
+            try {
+                getPackageManager().getPackageInstaller().abandonSession(sessionId);
+            } catch (SecurityException ignored) {
+            }
         }
     }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index c6217ec..7bea339 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -16,17 +16,17 @@
 
 package com.android.packageinstaller;
 
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+
 import android.app.PendingIntent;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.InstallInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Process;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -34,10 +34,7 @@
 import androidx.annotation.Nullable;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 
 /**
  * Send package to the package manager and handle results from package manager. Once the
@@ -77,7 +74,7 @@
                 .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
         mPackageURI = getIntent().getData();
 
-        if ("package".equals(mPackageURI.getScheme())) {
+        if (PackageInstallerActivity.SCHEME_PACKAGE.equals(mPackageURI.getScheme())) {
             try {
                 getPackageManager().installExistingPackage(appInfo.packageName);
                 launchSuccess();
@@ -86,6 +83,8 @@
                         PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
             }
         } else {
+            // ContentResolver.SCHEME_FILE
+            // STAGED_SESSION_ID extra contains an ID of a previously staged install session.
             final File sourceFile = new File(mPackageURI.getPath());
             PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
 
@@ -122,41 +121,6 @@
                     // Does not happen
                 }
             } else {
-                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-                final Uri referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
-                params.setPackageSource(
-                        referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
-                                : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
-                params.setInstallAsInstantApp(false);
-                params.setReferrerUri(referrerUri);
-                params.setOriginatingUri(getIntent()
-                        .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
-                params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
-                        Process.INVALID_UID));
-                params.setInstallerPackageName(getIntent().getStringExtra(
-                        Intent.EXTRA_INSTALLER_PACKAGE_NAME));
-                params.setInstallReason(PackageManager.INSTALL_REASON_USER);
-
-                File file = new File(mPackageURI.getPath());
-                try {
-                    final InstallInfo result = getPackageManager().getPackageInstaller()
-                            .readInstallInfo(file, 0);
-                    params.setAppPackageName(result.getPackageName());
-                    params.setInstallLocation(result.getInstallLocation());
-                    try {
-                        params.setSize(result.calculateInstalledSize(params));
-                    } catch (IOException e) {
-                        e.printStackTrace();
-                        params.setSize(file.length());
-                    }
-                } catch (PackageInstaller.PackageParsingException e) {
-
-                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.", e);
-                    Log.e(LOG_TAG,
-                            "Cannot calculate installed size " + file + ". Try only apk size.");
-                    params.setSize(file.length());
-                }
                 try {
                     mInstallId = InstallEventReceiver
                             .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
@@ -166,9 +130,14 @@
                             PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                 }
 
-                try {
-                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
-                } catch (IOException e) {
+                mSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
+                // Try to open session previously staged in InstallStaging.
+                try (PackageInstaller.Session ignored =
+                             getPackageManager().getPackageInstaller().openSession(
+                        mSessionId)) {
+                    Log.d(LOG_TAG, "Staged session is valid, proceeding with the install");
+                } catch (IOException | SecurityException e) {
+                    Log.e(LOG_TAG, "Invalid session id passed", e);
                     launchFailure(PackageInstaller.STATUS_FAILURE,
                             PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                 }
@@ -293,57 +262,9 @@
 
         @Override
         protected PackageInstaller.Session doInBackground(Void... params) {
-            PackageInstaller.Session session;
             try {
-                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
+                return getPackageManager().getPackageInstaller().openSession(mSessionId);
             } catch (IOException e) {
-                synchronized (this) {
-                    isDone = true;
-                    notifyAll();
-                }
-                return null;
-            }
-
-            session.setStagingProgress(0);
-
-            try {
-                File file = new File(mPackageURI.getPath());
-
-                try (InputStream in = new FileInputStream(file)) {
-                    long sizeBytes = file.length();
-                    long totalRead = 0;
-                    try (OutputStream out = session
-                            .openWrite("PackageInstaller", 0, sizeBytes)) {
-                        byte[] buffer = new byte[1024 * 1024];
-                        while (true) {
-                            int numRead = in.read(buffer);
-
-                            if (numRead == -1) {
-                                session.fsync(out);
-                                break;
-                            }
-
-                            if (isCancelled()) {
-                                session.close();
-                                break;
-                            }
-
-                            out.write(buffer, 0, numRead);
-                            if (sizeBytes > 0) {
-                                totalRead += numRead;
-                                float fraction = ((float) totalRead / (float) sizeBytes);
-                                session.setStagingProgress(fraction);
-                            }
-                        }
-                    }
-                }
-
-                return session;
-            } catch (IOException | SecurityException e) {
-                Log.e(LOG_TAG, "Could not write package", e);
-
-                session.close();
-
                 return null;
             } finally {
                 synchronized (this) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index 68de2f6..097e47f 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -16,6 +16,10 @@
 
 package com.android.packageinstaller;
 
+import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
+
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -23,40 +27,49 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.util.Log;
 import android.view.View;
+import android.widget.ProgressBar;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
 /**
- * If a package gets installed from an content URI this step loads the package and turns it into
- * and installation from a file. Then it re-starts the installation as usual.
+ * If a package gets installed from a content URI this step stages the installation session
+ * reading bytes from the URI.
  */
 public class InstallStaging extends AlertActivity {
     private static final String LOG_TAG = InstallStaging.class.getSimpleName();
 
-    private static final String STAGED_FILE = "STAGED_FILE";
+    private static final String STAGED_SESSION_ID = "STAGED_SESSION_ID";
+
+    private @Nullable PackageInstaller mInstaller;
 
     /** Currently running task that loads the file from the content URI into a file */
     private @Nullable StagingAsyncTask mStagingTask;
 
-    /** The file the package is in */
-    private @Nullable File mStagedFile;
+    /** The session the package is in */
+    private int mStagedSessionId;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        mInstaller = getPackageManager().getPackageInstaller();
+
         setFinishOnTouchOutside(true);
         mAlert.setIcon(R.drawable.ic_file_download);
         mAlert.setTitle(getString(R.string.app_name_unknown));
@@ -66,6 +79,9 @@
                     if (mStagingTask != null) {
                         mStagingTask.cancel(true);
                     }
+
+                    cleanupStagingSession();
+
                     setResult(RESULT_CANCELED);
                     finish();
                 }, null);
@@ -73,11 +89,7 @@
         requireViewById(R.id.staging).setVisibility(View.VISIBLE);
 
         if (savedInstanceState != null) {
-            mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));
-
-            if (!mStagedFile.exists()) {
-                mStagedFile = null;
-            }
+            mStagedSessionId = savedInstanceState.getInt(STAGED_SESSION_ID, 0);
         }
     }
 
@@ -85,21 +97,41 @@
     protected void onResume() {
         super.onResume();
 
-        // This is the first onResume in a single life of the activity
+        // This is the first onResume in a single life of the activity.
         if (mStagingTask == null) {
-            // File does not exist, or became invalid
-            if (mStagedFile == null) {
-                // Create file delayed to be able to show error
+            if (mStagedSessionId > 0) {
+                final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(
+                        mStagedSessionId);
+                if (info == null || !info.isActive() || info.getResolvedBaseApkPath() == null) {
+                    Log.w(LOG_TAG, "Session " + mStagedSessionId + " in funky state; ignoring");
+                    if (info != null) {
+                        cleanupStagingSession();
+                    }
+                    mStagedSessionId = 0;
+                }
+            }
+
+            // Session does not exist, or became invalid.
+            if (mStagedSessionId <= 0) {
+                // Create session here to be able to show error.
+                final Uri packageUri = getIntent().getData();
+                final AssetFileDescriptor afd = openAssetFileDescriptor(packageUri);
                 try {
-                    mStagedFile = TemporaryFileManager.getStagedFile(this);
+                    ParcelFileDescriptor pfd = afd != null ? afd.getParcelFileDescriptor() : null;
+                    PackageInstaller.SessionParams params = createSessionParams(
+                            mInstaller, getIntent(), pfd, packageUri.toString());
+                    mStagedSessionId = mInstaller.createSession(params);
                 } catch (IOException e) {
+                    Log.w(LOG_TAG, "Failed to create a staging session", e);
                     showError();
                     return;
+                } finally {
+                    PackageUtil.safeClose(afd);
                 }
             }
 
             mStagingTask = new StagingAsyncTask();
-            mStagingTask.execute(getIntent().getData());
+            mStagingTask.execute();
         }
     }
 
@@ -107,7 +139,7 @@
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
-        outState.putString(STAGED_FILE, mStagedFile.getPath());
+        outState.putInt(STAGED_SESSION_ID, mStagedSessionId);
     }
 
     @Override
@@ -119,6 +151,65 @@
         super.onDestroy();
     }
 
+    private AssetFileDescriptor openAssetFileDescriptor(Uri uri) {
+        try {
+            return getContentResolver().openAssetFileDescriptor(uri, "r");
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "Failed to open asset file descriptor", e);
+            return null;
+        }
+    }
+
+    private static PackageInstaller.SessionParams createSessionParams(
+            @NonNull PackageInstaller installer, @NonNull Intent intent,
+            @Nullable ParcelFileDescriptor pfd, @NonNull String debugPathName) {
+        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+        final Uri referrerUri = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+        params.setPackageSource(
+                referrerUri != null ? PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+                        : PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE);
+        params.setInstallAsInstantApp(false);
+        params.setReferrerUri(referrerUri);
+        params.setOriginatingUri(intent
+                .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
+        params.setOriginatingUid(intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+                Process.INVALID_UID));
+        params.setInstallerPackageName(intent.getStringExtra(
+                Intent.EXTRA_INSTALLER_PACKAGE_NAME));
+        params.setInstallReason(PackageManager.INSTALL_REASON_USER);
+
+        if (pfd != null) {
+            try {
+                final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd,
+                        debugPathName, 0);
+                params.setAppPackageName(result.getPackageName());
+                params.setInstallLocation(result.getInstallLocation());
+                params.setSize(result.calculateInstalledSize(params, pfd));
+            } catch (PackageInstaller.PackageParsingException | IOException e) {
+                Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.", e);
+                Log.e(LOG_TAG,
+                        "Cannot calculate installed size " + debugPathName
+                                + ". Try only apk size.");
+                params.setSize(pfd.getStatSize());
+            }
+        } else {
+            Log.e(LOG_TAG, "Cannot parse package " + debugPathName + ". Assuming defaults.");
+        }
+        return params;
+    }
+
+    private void cleanupStagingSession() {
+        if (mStagedSessionId > 0) {
+            try {
+                mInstaller.abandonSession(mStagedSessionId);
+            } catch (SecurityException ignored) {
+
+            }
+            mStagedSessionId = 0;
+        }
+    }
+
     /**
      * Show an error message and set result as error.
      */
@@ -165,58 +256,109 @@
         }
     }
 
-    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
-        @Override
-        protected Boolean doInBackground(Uri... params) {
-            if (params == null || params.length <= 0) {
-                return false;
-            }
-            Uri packageUri = params[0];
-            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
-                // Despite the comments in ContentResolver#openInputStream the returned stream can
-                // be null.
-                if (in == null) {
-                    return false;
-                }
+    private final class StagingAsyncTask extends
+            AsyncTask<Void, Integer, PackageInstaller.SessionInfo> {
+        private ProgressBar mProgressBar = null;
 
-                try (OutputStream out = new FileOutputStream(mStagedFile)) {
-                    byte[] buffer = new byte[1024 * 1024];
-                    int bytesRead;
-                    while ((bytesRead = in.read(buffer)) >= 0) {
-                        // Be nice and respond to a cancellation
-                        if (isCancelled()) {
-                            return false;
-                        }
-                        out.write(buffer, 0, bytesRead);
-                    }
-                }
-            } catch (IOException | SecurityException | IllegalStateException
-                    | IllegalArgumentException e) {
-                Log.w(LOG_TAG, "Error staging apk from content URI", e);
-                return false;
+        private long getContentSizeBytes() {
+            try (AssetFileDescriptor afd = openAssetFileDescriptor(getIntent().getData())) {
+                return afd != null ? afd.getLength() : UNKNOWN_LENGTH;
+            } catch (IOException ignored) {
+                return UNKNOWN_LENGTH;
             }
-            return true;
         }
 
         @Override
-        protected void onPostExecute(Boolean success) {
-            if (success) {
-                // Now start the installation again from a file
-                Intent installIntent = new Intent(getIntent());
-                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
-                installIntent.setData(Uri.fromFile(mStagedFile));
+        protected void onPreExecute() {
+            final long sizeBytes = getContentSizeBytes();
 
-                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
-                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            mProgressBar = sizeBytes > 0 ? requireViewById(R.id.progress_indeterminate) : null;
+            if (mProgressBar != null) {
+                mProgressBar.setProgress(0);
+                mProgressBar.setMax(100);
+                mProgressBar.setIndeterminate(false);
+            }
+        }
+
+        @Override
+        protected PackageInstaller.SessionInfo doInBackground(Void... params) {
+            Uri packageUri = getIntent().getData();
+            try (PackageInstaller.Session session = mInstaller.openSession(mStagedSessionId);
+                 InputStream in = getContentResolver().openInputStream(packageUri)) {
+                session.setStagingProgress(0);
+
+                if (in == null) {
+                    return null;
                 }
 
-                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
-                startActivity(installIntent);
+                long sizeBytes = getContentSizeBytes();
 
-                InstallStaging.this.finish();
-            } else {
-                showError();
+                long totalRead = 0;
+                try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
+                    byte[] buffer = new byte[1024 * 1024];
+                    while (true) {
+                        int numRead = in.read(buffer);
+
+                        if (numRead == -1) {
+                            session.fsync(out);
+                            break;
+                        }
+
+                        if (isCancelled()) {
+                            break;
+                        }
+
+                        out.write(buffer, 0, numRead);
+                        if (sizeBytes > 0) {
+                            totalRead += numRead;
+                            float fraction = ((float) totalRead / (float) sizeBytes);
+                            session.setStagingProgress(fraction);
+                            publishProgress((int) (fraction * 100.0));
+                        }
+                    }
+                }
+
+                return mInstaller.getSessionInfo(mStagedSessionId);
+            } catch (IOException | SecurityException | IllegalStateException
+                     | IllegalArgumentException e) {
+                Log.w(LOG_TAG, "Error staging apk from content URI", e);
+                return null;
             }
         }
+
+        @Override
+        protected void onProgressUpdate(Integer... progress) {
+            if (mProgressBar != null && progress != null && progress.length > 0) {
+                mProgressBar.setProgress(progress[0], true);
+            }
+        }
+
+        @Override
+        protected void onPostExecute(PackageInstaller.SessionInfo sessionInfo) {
+            if (sessionInfo == null || !sessionInfo.isActive()
+                    || sessionInfo.getResolvedBaseApkPath() == null) {
+                Log.w(LOG_TAG, "Session info is invalid: " + sessionInfo);
+                cleanupStagingSession();
+                showError();
+                return;
+            }
+
+            // Pass the staged session to the installer.
+            Intent installIntent = new Intent(getIntent());
+            installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
+            installIntent.setData(Uri.fromFile(new File(sessionInfo.getResolvedBaseApkPath())));
+
+            installIntent.putExtra(EXTRA_STAGED_SESSION_ID, mStagedSessionId);
+
+            if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+                installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            }
+
+            installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+            startActivity(installIntent);
+
+            InstallStaging.this.finish();
+        }
     }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index d0f4e21..3f98867 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -163,10 +163,11 @@
                 // [IMPORTANT] This path is deprecated, but should still work. Only necessary
                 // features should be added.
 
-                // Copy file to prevent it from being changed underneath this process
+                // Stage a session with this file to prevent it from being changed underneath
+                // this process.
                 nextActivity.setClass(this, InstallStaging.class);
-            } else if (packageUri != null && packageUri.getScheme().equals(
-                    PackageInstallerActivity.SCHEME_PACKAGE)) {
+            } else if (packageUri != null && PackageInstallerActivity.SCHEME_PACKAGE.equals(
+                    packageUri.getScheme())) {
                 nextActivity.setClass(this, PackageInstallerActivity.class);
             } else {
                 Intent result = new Intent();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 3ba2acb..7e294ee 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -82,6 +82,7 @@
     static final String EXTRA_CALLING_PACKAGE = "EXTRA_CALLING_PACKAGE";
     static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
     static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
+    static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
     private static final String ALLOW_UNKNOWN_SOURCES_KEY =
             PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
 
@@ -403,6 +404,10 @@
             mReferrerURI = null;
             mPendingUserActionReason = info.getPendingUserActionReason();
         } else {
+            // Two possible callers:
+            // 1. InstallStart with "SCHEME_PACKAGE".
+            // 2. InstallStaging with "SCHEME_FILE" and EXTRA_STAGED_SESSION_ID with staged
+            // session id.
             mSessionId = -1;
             packageSource = intent.getData();
             mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
@@ -721,14 +726,16 @@
     }
 
     private void startInstall() {
+        String installerPackageName = getIntent().getStringExtra(
+                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+        int stagedSessionId = getIntent().getIntExtra(EXTRA_STAGED_SESSION_ID, 0);
+
         // Start subactivity to actually install the application
         Intent newIntent = new Intent();
         newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                 mPkgInfo.applicationInfo);
         newIntent.setData(mPackageURI);
         newIntent.setClass(this, InstallInstalling.class);
-        String installerPackageName = getIntent().getStringExtra(
-                Intent.EXTRA_INSTALLER_PACKAGE_NAME);
         if (mOriginatingURI != null) {
             newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
         }
@@ -745,6 +752,9 @@
         if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
             newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
         }
+        if (stagedSessionId > 0) {
+            newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
+        }
         newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
         if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
         startActivity(newIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index 698159f..0270591 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -33,7 +33,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import java.io.Closeable;
 import java.io.File;
+import java.io.IOException;
 
 /**
  * This is a utility class for defining some utility methods and constants
@@ -190,4 +192,19 @@
         }
         return targetSdkVersion;
     }
+
+
+    /**
+     * Quietly close a closeable resource (e.g. a stream or file). The input may already
+     * be closed and it may even be null.
+     */
+    static void safeClose(Closeable resource) {
+        if (resource != null) {
+            try {
+                resource.close();
+            } catch (IOException ioe) {
+                // Catch and discard the error
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
index 1838663..191158e 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml
@@ -20,7 +20,8 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/udfps_animation_view_internal"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:contentDescription="@string/accessibility_fingerprint_label">
 
     <!-- Background protection -->
     <ImageView
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 3579e8c..fd9cee0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -829,9 +829,9 @@
 
             final Rect overlayBounds = new Rect(
                     0, /* left */
-                    0, /* top */
+                    mCachedDisplayInfo.getNaturalHeight() / 2, /* top */
                     mCachedDisplayInfo.getNaturalWidth(), /* right */
-                    mCachedDisplayInfo.getNaturalHeight() /* botom */);
+                    mCachedDisplayInfo.getNaturalHeight() /* bottom */);
 
             mUdfpsOverlayParams = new UdfpsOverlayParams(
                     mUdfpsBounds,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index f876aff..d953a88 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -364,7 +364,12 @@
                 if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) {
                     Rect(overlayParams.sensorBounds)
                 } else {
-                    Rect(overlayParams.overlayBounds)
+                    Rect(
+                        0,
+                        0,
+                        overlayParams.naturalDisplayWidth,
+                        overlayParams.naturalDisplayHeight
+                    )
                 }
             } else {
                 Rect(overlayParams.sensorBounds)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 2747e83..8a62ea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -25,7 +25,6 @@
 import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
 import android.hardware.fingerprint.FingerprintManager
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import android.testing.TestableLooper.RunWithLooper
 import android.view.LayoutInflater
 import android.view.MotionEvent
@@ -35,6 +34,7 @@
 import android.view.View
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.settingslib.udfps.UdfpsOverlayParams
@@ -69,8 +69,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val REQUEST_ID = 2L
 
@@ -340,4 +340,22 @@
             assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
         }
     }
+
+    @Test
+    fun fullScreenOverlayWithNewTouchDetectionEnabled() = withRotation(ROTATION_0) {
+        withReason(REASON_AUTH_KEYGUARD) {
+            whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
+
+            controllerOverlay.show(udfpsController, overlayParams)
+            verify(windowManager).addView(
+                    eq(controllerOverlay.overlayView),
+                    layoutParamsCaptor.capture()
+            )
+
+            // Layout params should use natural display width and height
+            val lp = layoutParamsCaptor.value
+            assertThat(lp.width).isEqualTo(overlayParams.naturalDisplayWidth)
+            assertThat(lp.height).isEqualTo(overlayParams.naturalDisplayHeight)
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index 490a023..7908907 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -34,7 +34,7 @@
 import android.content.ComponentName;
 import android.content.pm.ServiceInfo;
 import android.util.ArrayMap;
-import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -192,13 +192,20 @@
         final ArrayList<Integer> apiTypes = convertFgsTypeToApiTypes(record.foregroundServiceType);
         final UidState uidState = mUids.get(uid);
         if (uidState == null) {
-            Log.e(TAG, "FGS stop call being logged with no start call for UID " + uid);
+            Slog.wtfStack(TAG, "FGS stop call being logged with no start call for UID for UID "
+                    + uid
+                    + " in package " + record.packageName);
             return;
         }
         final ArrayList<Integer> apisFound = new ArrayList<>();
         final ArrayList<Long> timestampsFound = new ArrayList<>();
         for (int i = 0, size = apiTypes.size(); i < size; i++) {
-            int apiType = apiTypes.get(i);
+            final int apiType = apiTypes.get(i);
+            if (!uidState.mOpenWithFgsCount.contains(apiType)) {
+                Slog.wtfStack(TAG, "Logger should be tracking FGS types correctly for UID " + uid
+                        + " in package " + record.packageName);
+                continue;
+            }
             // retrieve the eligible closed call
             // we only want to log if this is the only
             // open in flight call. If there are other calls,
@@ -215,7 +222,8 @@
             final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
                     uidState.mRunningFgs.get(apiType);
             if (runningFgsOfType == null) {
-                Log.w(TAG, "Could not find appropriate running FGS for FGS stop");
+                Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
+                        + " in package " + record.packageName);
                 continue;
             }
 
@@ -322,7 +330,7 @@
         // it's not related to any FGS
         UidState uidState = mUids.get(uid);
         if (uidState == null) {
-            Log.w(TAG, "API event end called before start!");
+            Slog.w(TAG, "API event end called before start!");
             return -1;
         }
         if (uidState.mOpenWithFgsCount.contains(apiType)) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 29c5ada..97e7f6f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1148,12 +1148,21 @@
             info.userId = userId;
             info.installerPackageName = mInstallSource.mInstallerPackageName;
             info.installerAttributionTag = mInstallSource.mInstallerAttributionTag;
+            info.resolvedBaseCodePath = null;
             if (mContext.checkCallingOrSelfPermission(
                     Manifest.permission.READ_INSTALLED_SESSION_PATHS)
-                            == PackageManager.PERMISSION_GRANTED && mResolvedBaseFile != null) {
-                info.resolvedBaseCodePath = mResolvedBaseFile.getAbsolutePath();
-            } else {
-                info.resolvedBaseCodePath = null;
+                    == PackageManager.PERMISSION_GRANTED) {
+                File file = mResolvedBaseFile;
+                if (file == null) {
+                    // Try to guess mResolvedBaseFile file.
+                    final List<File> addedFiles = getAddedApksLocked();
+                    if (addedFiles.size() > 0) {
+                        file = addedFiles.get(0);
+                    }
+                }
+                if (file != null) {
+                    info.resolvedBaseCodePath = file.getAbsolutePath();
+                }
             }
             info.progress = progress;
             info.sealed = mSealed;
@@ -1355,9 +1364,12 @@
 
     @GuardedBy("mLock")
     private String[] getStageDirContentsLocked() {
+        if (stageDir == null) {
+            return EmptyArray.STRING;
+        }
         String[] result = stageDir.list();
         if (result == null) {
-            result = EmptyArray.STRING;
+            return EmptyArray.STRING;
         }
         return result;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25e5dac..24a271f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4908,9 +4908,14 @@
             mTransitionController.setStatusBarTransitionDelay(
                     mPendingRemoteAnimation.getStatusBarTransitionDelay());
         } else {
-            if (mPendingOptions == null
-                    || mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) {
-                // Scene transition will run on the client side.
+            if (mPendingOptions == null) {
+                return;
+            } else if (mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) {
+                // Scene transition will run on the client side, so just notify transition
+                // controller but don't clear the animation information from the options since they
+                // need to be sent to the animating activity.
+                mTransitionController.setOverrideAnimation(
+                        AnimationOptions.makeSceneTransitionAnimOptions(), null, null);
                 return;
             }
             applyOptionsAnimation(mPendingOptions, intent);
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index f05b1d4..475966e 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -20,9 +20,11 @@
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
@@ -53,7 +55,8 @@
     public static final String LOG_TAG = "ProfcollectForwardingService";
 
     private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
-
+    private static final String INTENT_UPLOAD_PROFILES =
+            "com.android.server.profcollect.UPLOAD_PROFILES";
     private static final long BG_PROCESS_PERIOD = TimeUnit.HOURS.toMillis(4); // every 4 hours.
 
     private IProfCollectd mIProfcollect;
@@ -66,6 +69,16 @@
         }
     };
 
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction() == INTENT_UPLOAD_PROFILES) {
+                Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
+                packAndUploadReport();
+            }
+        }
+    };
+
     public ProfcollectForwardingService(Context context) {
         super(context);
 
@@ -73,6 +86,10 @@
             throw new AssertionError("only one service instance allowed");
         }
         sSelfService = this;
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(INTENT_UPLOAD_PROFILES);
+        context.registerReceiver(mBroadcastReceiver, filter);
     }
 
     /**
@@ -296,7 +313,7 @@
                 }
 
                 if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) {
-                    packProfileReport();
+                    packAndUploadReport();
                 }
             }
 
@@ -307,7 +324,7 @@
         });
     }
 
-    private void packProfileReport() {
+    private void packAndUploadReport() {
         if (mIProfcollect == null) {
             return;
         }