Merge "remove unneeded RecordedOps"
diff --git a/api/system-current.txt b/api/system-current.txt
index d62c43e..ea1f1a1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1062,7 +1062,11 @@
   }
 
   public class PackageItemInfo {
-    method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
+    method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
+    method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
+    field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
+    field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
+    field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
   }
 
   public abstract class PackageManager {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3a28bdf..41a39fd 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -922,6 +922,7 @@
 Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
 Landroid/content/pm/PackageUserState;-><init>()V
 Landroid/content/pm/ParceledListSlice;-><init>(Ljava/util/List;)V
+Landroid/content/pm/ShortcutInfo;->getIcon()Landroid/graphics/drawable/Icon;
 Landroid/content/pm/ShortcutManager;->mService:Landroid/content/pm/IShortcutService;
 Landroid/content/pm/Signature;->getPublicKey()Ljava/security/PublicKey;
 Landroid/content/pm/UserInfo;-><init>(ILjava/lang/String;I)V
@@ -2500,6 +2501,7 @@
 Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
 Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
+Landroid/speech/tts/TtsEngines;-><init>(Landroid/content/Context;)V
 Landroid/system/Int32Ref;->value:I
 Landroid/system/OsConstants;-><init>()V
 Landroid/system/OsConstants;->AF_NETLINK:I
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8c0cd23..4b84ed4 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -64,6 +64,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -668,7 +669,17 @@
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
         String libraryPermittedPath = mDataDir;
+
         if (isBundledApp) {
+            // For bundled apps, add the base directory of the app (e.g.,
+            // /system/app/Foo/) to the permitted paths so that it can load libraries
+            // embedded in module apks under the directory. For now, GmsCore is relying
+            // on this, but this isn't specific to the app. Also note that, we don't
+            // need to do this for unbundled apps as entire /data is already set to
+            // the permitted paths for them.
+            libraryPermittedPath += File.pathSeparator
+                    + Paths.get(getAppDir()).getParent().toString();
+
             // This is necessary to grant bundled apps access to
             // libraries located in subdirectories of /system/lib
             libraryPermittedPath += File.pathSeparator + defaultSearchPaths;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08e2c91..22367b2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3725,7 +3725,7 @@
      */
     public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER =
             DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
-            | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+            | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS;
 
     /**
      * Called by an application that is administering the device to request that the storage system
@@ -4738,12 +4738,14 @@
      * <ul>
      * <li>{@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which affects the parent user, but only if there
      * is no separate challenge set on the managed profile.
-     * <li>{@link #KEYGUARD_DISABLE_FINGERPRINT} which affects the managed profile challenge if
+     * <li>{@link #KEYGUARD_DISABLE_FINGERPRINT}, {@link #KEYGUARD_DISABLE_FACE} or
+     * {@link #KEYGUARD_DISABLE_IRIS} which affects the managed profile challenge if
      * there is one, or the parent user otherwise.
      * <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} which affects notifications generated
      * by applications in the managed profile.
      * </ul>
-     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} and {@link #KEYGUARD_DISABLE_FINGERPRINT} can also be
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT},
+     * {@link #KEYGUARD_DISABLE_FACE} and {@link #KEYGUARD_DISABLE_IRIS} can also be
      * set on the {@link DevicePolicyManager} instance returned by
      * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
      * profile.
@@ -4754,12 +4756,16 @@
      * {@link #getKeyguardDisabledFeatures(ComponentName)}
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param which {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default),
+     * @param which The disabled features flag which can be either
+     *            {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default),
+     *            {@link #KEYGUARD_DISABLE_FEATURES_ALL}, or a combination of
      *            {@link #KEYGUARD_DISABLE_WIDGETS_ALL}, {@link #KEYGUARD_DISABLE_SECURE_CAMERA},
      *            {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS},
      *            {@link #KEYGUARD_DISABLE_TRUST_AGENTS},
      *            {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS},
-     *            {@link #KEYGUARD_DISABLE_FINGERPRINT}, {@link #KEYGUARD_DISABLE_FEATURES_ALL}
+     *            {@link #KEYGUARD_DISABLE_FINGERPRINT},
+     *            {@link #KEYGUARD_DISABLE_FACE},
+     *            {@link #KEYGUARD_DISABLE_IRIS}.
      * @throws SecurityException if {@code admin} is not an active administrator or does not user
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES}
      */
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 6e633426..a10cc12 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -261,6 +261,7 @@
         result = prime * result + state;
         result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
         result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
+        result = prime * result + ((category == null) ? 0 : category.hashCode());
         result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
         return result;
     }
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 07fbfb5..14d3f91 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -16,6 +16,10 @@
 
 package android.content.pm;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.res.XmlResourceParser;
@@ -29,7 +33,11 @@
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
 import java.text.Collator;
+import java.util.BitSet;
 import java.util.Comparator;
 
 /**
@@ -42,10 +50,56 @@
  * in the implementation of Parcelable in subclasses.
  */
 public class PackageItemInfo {
-    private static final float MAX_LABEL_SIZE_PX = 500f;
+    private static final int LINE_FEED_CODE_POINT = 10;
+    private static final int NBSP_CODE_POINT = 160;
+
     /** The maximum length of a safe label, in characters */
     private static final int MAX_SAFE_LABEL_LENGTH = 50000;
 
+    /** @hide */
+    public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
+
+    /**
+     * Flags for {@link #loadSafeLabel(PackageManager, float, int)}
+     *
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_",
+            value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE,
+                    SAFE_LABEL_FLAG_FIRST_LINE})
+    public @interface SafeLabelFlags {}
+
+    /**
+     * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
+     * of the label.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     * @hide
+     */
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_TRIM = 0x1;
+
+    /**
+     * Force entire string into single line of text (no newlines). Cannot be set at the same time as
+     * {@link #SAFE_LABEL_FLAG_FIRST_LINE}.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     * @hide
+     */
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2;
+
+    /**
+     * Return only first line of text (truncate at first newline). Cannot be set at the same time as
+     * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}.
+     *
+     * @see #loadSafeLabel(PackageManager, float, int)
+     * @hide
+     */
+    @SystemApi
+    public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4;
+
     private static volatile boolean sForceSafeLabels = false;
 
     /** {@hide} */
@@ -140,7 +194,8 @@
      */
     public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
         if (sForceSafeLabels) {
-            return loadSafeLabel(pm);
+            return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
+                    | SAFE_LABEL_FLAG_FIRST_LINE);
         } else {
             return loadUnsafeLabel(pm);
         }
@@ -163,66 +218,226 @@
         return packageName;
     }
 
+    private static boolean isNewline(int codePoint) {
+        int type = Character.getType(codePoint);
+        return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
+                || codePoint == LINE_FEED_CODE_POINT;
+    }
+
+    private static boolean isWhiteSpace(int codePoint) {
+        return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
+    }
+
     /**
-     * Same as {@link #loadLabel(PackageManager)} with the addition that
-     * the returned label is safe for being presented in the UI since it
-     * will not contain new lines and the length will be limited to a
-     * reasonable amount. This prevents a malicious party to influence UI
-     * layout via the app label misleading the user into performing a
-     * detrimental for them action. If the label is too long it will be
-     * truncated and ellipsized at the end.
+     * @hide
+     * @deprecated use loadSafeLabel(PackageManager, float, int) instead
+     */
+    @SystemApi
+    @Deprecated
+    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
+        return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
+                | SAFE_LABEL_FLAG_FIRST_LINE);
+    }
+
+    /**
+     * A special string manipulation class. Just records removals and executes the when onString()
+     * is called.
+     */
+    private static class StringWithRemovedChars {
+        /** The original string */
+        private final String mOriginal;
+
+        /**
+         * One bit per char in string. If bit is set, character needs to be removed. If whole
+         * bit field is not initialized nothing needs to be removed.
+         */
+        private BitSet mRemovedChars;
+
+        StringWithRemovedChars(@NonNull String original) {
+            mOriginal = original;
+        }
+
+        /**
+         * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
+         * firstNonRemoved) as removed.
+         */
+        void removeRange(int firstRemoved, int firstNonRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(firstRemoved, firstNonRemoved);
+        }
+
+        /**
+         * Remove all characters before {@code firstNonRemoved}.
+         */
+        void removeAllCharBefore(int firstNonRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(0, firstNonRemoved);
+        }
+
+        /**
+         * Remove all characters after and including {@code firstRemoved}.
+         */
+        void removeAllCharAfter(int firstRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(firstRemoved, mOriginal.length());
+        }
+
+        @Override
+        public String toString() {
+            // Common case, no chars removed
+            if (mRemovedChars == null) {
+                return mOriginal;
+            }
+
+            StringBuilder sb = new StringBuilder(mOriginal.length());
+            for (int i = 0; i < mOriginal.length(); i++) {
+                if (!mRemovedChars.get(i)) {
+                    sb.append(mOriginal.charAt(i));
+                }
+            }
+
+            return sb.toString();
+        }
+
+        /**
+         * Return length or the original string
+         */
+        int length() {
+            return mOriginal.length();
+        }
+
+        /**
+         * Return if a certain {@code offset} of the original string is removed
+         */
+        boolean isRemoved(int offset) {
+            return mRemovedChars != null && mRemovedChars.get(offset);
+        }
+
+        /**
+         * Return codePoint of original string at a certain {@code offset}
+         */
+        int codePointAt(int offset) {
+            return mOriginal.codePointAt(offset);
+        }
+    }
+
+    /**
+     * Load, clean up and truncate label before use.
      *
-     * @param pm A PackageManager from which the label can be loaded; usually
-     * the PackageManager from which you originally retrieved this item
-     * @return Returns a CharSequence containing the item's label. If the
-     * item does not have a label, its name is returned.
+     * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
+     * are used in sensitive parts of the UI.
      *
+     * <p>This method first treats the string like HTML and then ...
+     * <ul>
+     * <li>Removes new lines or truncates at first new line
+     * <li>Trims the white-space off the end
+     * <li>Truncates the string to a given length
+     * </ul>
+     * ... if specified.
+     *
+     * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
+     *                     This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
+     *                     Usually ellipsizing should be left to the view showing the string. If a
+     *                     string is used as an input to another string, it might be useful to
+     *                     control the length of the input string though. {@code 0} disables this
+     *                     feature.
+     * @return The safe label
      * @hide
      */
     @SystemApi
-    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
+    public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm,
+            @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) {
+        boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0);
+        boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0);
+        boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0);
+
+        Preconditions.checkNotNull(pm);
+        Preconditions.checkArgument(ellipsizeDip >= 0);
+        Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE
+                | SAFE_LABEL_FLAG_FIRST_LINE);
+        Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
+                "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same "
+                + "time");
+
         // loadLabel() always returns non-null
         String label = loadUnsafeLabel(pm).toString();
-        // strip HTML tags to avoid <br> and other tags overwriting original message
-        String labelStr = Html.fromHtml(label).toString();
 
-        // If the label contains new line characters it may push the UI
-        // down to hide a part of it. Labels shouldn't have new line
-        // characters, so just truncate at the first time one is seen.
-        final int labelLength = Math.min(labelStr.length(), MAX_SAFE_LABEL_LENGTH);
-        final StringBuffer sb = new StringBuffer(labelLength);
-        int offset = 0;
-        while (offset < labelLength) {
-            final int codePoint = labelStr.codePointAt(offset);
-            final int type = Character.getType(codePoint);
-            if (type == Character.LINE_SEPARATOR
-                    || type == Character.CONTROL
-                    || type == Character.PARAGRAPH_SEPARATOR) {
-                labelStr = labelStr.substring(0, offset);
+        // Treat string as HTML. This
+        // - converts HTML symbols: e.g. &szlig; -> ß
+        // - applies some HTML tags: e.g. <br> -> \n
+        // - removes invalid characters such as \b
+        // - removes html styling, such as <b>
+        // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
+        // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
+        // - Removes leading white space
+        // - Removes all trailing white space beside a single space
+        // - Collapses double white space
+        StringWithRemovedChars labelStr = new StringWithRemovedChars(
+                Html.fromHtml(label).toString());
+
+        int firstNonWhiteSpace = -1;
+        int firstTrailingWhiteSpace = -1;
+
+        // Remove new lines (if requested) and control characters.
+        int labelLength = labelStr.length();
+        for (int offset = 0; offset < labelLength; ) {
+            int codePoint = labelStr.codePointAt(offset);
+            int type = Character.getType(codePoint);
+            int codePointLen = Character.charCount(codePoint);
+            boolean isNewline = isNewline(codePoint);
+
+            if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) {
+                labelStr.removeAllCharAfter(offset);
                 break;
+            } else if (forceSingleLine && isNewline) {
+                labelStr.removeRange(offset, offset + codePointLen);
+            } else if (type == Character.CONTROL && !isNewline) {
+                labelStr.removeRange(offset, offset + codePointLen);
+            } else if (trim && !isWhiteSpace(codePoint)) {
+                // This is only executed if the code point is not removed
+                if (firstNonWhiteSpace == -1) {
+                    firstNonWhiteSpace = offset;
+                }
+                firstTrailingWhiteSpace = offset + codePointLen;
             }
-            // replace all non-break space to " " in order to be trimmed
-            final int charCount = Character.charCount(codePoint);
-            if (type == Character.SPACE_SEPARATOR) {
-                sb.append(' ');
+
+            offset += codePointLen;
+        }
+
+        if (trim) {
+            // Remove leading and trailing white space
+            if (firstNonWhiteSpace == -1) {
+                // No non whitespace found, remove all
+                labelStr.removeAllCharAfter(0);
             } else {
-                sb.append(labelStr.charAt(offset));
-                if (charCount == 2) {
-                    sb.append(labelStr.charAt(offset + 1));
+                if (firstNonWhiteSpace > 0) {
+                    labelStr.removeAllCharBefore(firstNonWhiteSpace);
+                }
+                if (firstTrailingWhiteSpace < labelLength) {
+                    labelStr.removeAllCharAfter(firstTrailingWhiteSpace);
                 }
             }
-            offset += charCount;
         }
 
-        labelStr = sb.toString().trim();
-        if (labelStr.isEmpty()) {
-            return packageName;
-        }
-        TextPaint paint = new TextPaint();
-        paint.setTextSize(42);
+        if (ellipsizeDip == 0) {
+            return labelStr.toString();
+        } else {
+            // Truncate
+            final TextPaint paint = new TextPaint();
+            paint.setTextSize(42);
 
-        return TextUtils.ellipsize(labelStr, paint, MAX_LABEL_SIZE_PX,
-                TextUtils.TruncateAt.END);
+            return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip,
+                    TextUtils.TruncateAt.END);
+        }
     }
 
     /**
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 1f2b90a..3820798 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -282,7 +282,12 @@
             @Nullable SelectionResult result) {
         final Layout layout = mTextView.getLayout();
 
-        final Runnable onAnimationEndCallback = () -> startSelectionActionMode(result);
+        final String originalText = getText(mTextView).toString();
+        final Runnable onAnimationEndCallback = () -> {
+            if (TextUtils.equals(getText(mTextView), originalText)) {
+                startSelectionActionMode(result);
+            }
+        };
         // TODO do not trigger the animation if the change included only non-printable characters
         final boolean didSelectionChange =
                 result != null && (mTextView.getSelectionStart() != result.mStart
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5380cd8..4a2c2c5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2284,7 +2284,7 @@
      * Sets the key listener to be used with this TextView.  This can be null
      * to disallow user input.  Note that this method has significant and
      * subtle interactions with soft keyboards and other input method:
-     * see {@link KeyListener#getInputType() KeyListener.getContentType()}
+     * see {@link KeyListener#getInputType() KeyListener.getInputType()}
      * for important details.  Calling this method will replace the current
      * content type of the text view with the content type returned by the
      * key listener.
diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
index 9966626..ce2d229 100644
--- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
+++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.util.Log;
@@ -82,7 +83,10 @@
         final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
                 null /*root*/);
         ((TextView) view.findViewById(R.id.app_name_text))
-                .setText(applicationInfo.loadSafeLabel(getPackageManager()));
+                .setText(applicationInfo.loadSafeLabel(getPackageManager(),
+                        PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                        PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                                | PackageItemInfo.SAFE_LABEL_FLAG_TRIM));
         ((TextView) view.findViewById(R.id.message))
                 .setText(mHarmfulAppWarning);
         return view;
diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp
index dd9bafe..462d052 100644
--- a/core/jni/android/graphics/Utils.cpp
+++ b/core/jni/android/graphics/Utils.cpp
@@ -49,6 +49,38 @@
     return NULL;
 }
 
+bool AssetStreamAdaptor::hasPosition() const {
+    return fAsset->seek(0, SEEK_CUR) != -1;
+}
+
+size_t AssetStreamAdaptor::getPosition() const {
+    const off64_t offset = fAsset->seek(0, SEEK_CUR);
+    if (offset == -1) {
+        SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n");
+        return 0;
+    }
+
+    return offset;
+}
+
+bool AssetStreamAdaptor::seek(size_t position) {
+    if (fAsset->seek(position, SEEK_SET) == -1) {
+        SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n");
+        return false;
+    }
+
+    return true;
+}
+
+bool AssetStreamAdaptor::move(long offset) {
+    if (fAsset->seek(offset, SEEK_CUR) == -1) {
+        SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+        return false;
+    }
+
+    return true;
+}
+
 size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
     ssize_t amount;
 
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
index 2f2ee96..ac291ea 100644
--- a/core/jni/android/graphics/Utils.h
+++ b/core/jni/android/graphics/Utils.h
@@ -34,6 +34,10 @@
     virtual size_t read(void* buffer, size_t size);
     virtual bool hasLength() const { return true; }
     virtual size_t getLength() const;
+    virtual bool hasPosition() const;
+    virtual size_t getPosition() const;
+    virtual bool seek(size_t position);
+    virtual bool move(long offset);
     virtual bool isAtEnd() const;
 
 protected:
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 62d78e7..f0da660 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -219,8 +219,20 @@
     SkPaint tmpPaint;
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageLattice(image.get(), lattice, dst,
-                               bitmapPaint(paint, &tmpPaint, colorFilter));
+    const SkPaint* filteredPaint = bitmapPaint(paint, &tmpPaint, colorFilter);
+    // Besides kNone, the other three SkFilterQualities are treated the same. And Android's
+    // Java API only supports kLow and kNone anyway.
+    if (!filteredPaint || filteredPaint->getFilterQuality() == kNone_SkFilterQuality) {
+        if (filteredPaint != &tmpPaint) {
+            if (paint) {
+                tmpPaint = *paint;
+            }
+            filteredPaint = &tmpPaint;
+        }
+        tmpPaint.setFilterQuality(kLow_SkFilterQuality);
+    }
+
+    mRecorder.drawImageLattice(image.get(), lattice, dst, filteredPaint);
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index a2d2ddf..f878822 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -265,7 +265,7 @@
             mFile = new AtomicFile(new File(new File(
                     Environment.getDataUserCePackageDirectory(
                             StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
-                    "assistant"), "block_stats.xml"));
+                    "assistant"), "blocking_helper_stats.xml"));
             loadFile();
             for (StatusBarNotification sbn : getActiveNotifications()) {
                 onNotificationPosted(sbn);
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
index 8908ebd..29ee920 100644
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -32,7 +32,7 @@
     private static final String TAG = "ExtAssistant.CI";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .4f;
+    static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .8f;
     static final int DEFAULT_STREAK_LIMIT = 2;
     static final String ATT_DISMISSALS = "dismisses";
     static final String ATT_VIEWS = "views";
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 56cb888..e6ea2d8 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -21,6 +21,7 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.DialogInterface.OnDismissListener;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.Uri;
@@ -51,10 +52,14 @@
 
         try {
             PackageManager pm = getPackageManager();
-            CharSequence app1 = BidiFormatter.getInstance().unicodeWrap(
-                    pm.getApplicationInfo(mCallingPkg, 0).loadSafeLabel(pm).toString());
-            CharSequence app2 = BidiFormatter.getInstance().unicodeWrap(
-                    pm.getApplicationInfo(mProviderPkg, 0).loadSafeLabel(pm).toString());
+            CharSequence app1 = BidiFormatter.getInstance().unicodeWrap(pm.getApplicationInfo(
+                    mCallingPkg, 0).loadSafeLabel(pm, PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                    PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE).toString());
+            CharSequence app2 = BidiFormatter.getInstance().unicodeWrap(pm.getApplicationInfo(
+                    mProviderPkg, 0).loadSafeLabel(pm, PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                    PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE).toString());
             AlertDialog dialog = new AlertDialog.Builder(this)
                     .setTitle(getString(R.string.slice_permission_title, app1, app2))
                     .setView(R.layout.slice_permission_request)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 8adf4bc..3454fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -469,6 +469,7 @@
         updateNotificationColor();
         if (mMenuRow != null) {
             mMenuRow.onNotificationUpdated(mStatusBarNotification);
+            mMenuRow.setAppName(mAppName);
         }
         if (mIsSummaryWithChildren) {
             mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index d44fe4d..3865b27 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -23,7 +23,6 @@
 import static com.android.internal.util.Preconditions.checkState;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
-import android.Manifest;
 import android.annotation.CheckResult;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -39,6 +38,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.net.NetworkPolicyManager;
 import android.os.Binder;
@@ -289,7 +289,10 @@
             String packageTitle = BidiFormatter.getInstance().unicodeWrap(
                     getPackageInfo(callingPackage, userId)
                             .applicationInfo
-                            .loadSafeLabel(getContext().getPackageManager())
+                            .loadSafeLabel(getContext().getPackageManager(),
+                                    PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                                    PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+                                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE)
                             .toString());
             long identity = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a4d0dc8..74b4543 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -812,9 +812,9 @@
 
                 LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis(
                         KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
+                        !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L);
                 LIGHT_PRE_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_PRE_IDLE_TIMEOUT,
-                        !COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L);
+                        !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L);
                 LIGHT_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
                 LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR,
diff --git a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
index d9878cd..e5add58 100644
--- a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
+++ b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
@@ -23,13 +23,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.SystemPropertiesProto;
 import android.util.Log;
 import android.view.Window;
 import android.view.WindowManager;
-import android.widget.CheckBox;
 
 import com.android.internal.R;
 import com.android.server.utils.AppInstallerUtil;
@@ -45,7 +44,10 @@
         mPackageName = appInfo.packageName;
 
         final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm);
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
         final CharSequence message = context.getString(R.string.deprecated_target_sdk_message);
 
         final AlertDialog.Builder builder = new AlertDialog.Builder(context)
diff --git a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java b/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
index b6f6ae6..7348a0d 100644
--- a/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
+++ b/services/core/java/com/android/server/am/UnsupportedCompileSdkDialog.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.view.Window;
 import android.view.WindowManager;
@@ -37,7 +38,10 @@
         mPackageName = appInfo.packageName;
 
         final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm);
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
         final CharSequence message = context.getString(R.string.unsupported_compile_sdk_message,
                 label);
 
diff --git a/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java b/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java
index 8850663..1d6438c 100644
--- a/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java
+++ b/services/core/java/com/android/server/am/UnsupportedDisplaySizeDialog.java
@@ -21,6 +21,7 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.view.Window;
 import android.view.WindowManager;
@@ -35,7 +36,10 @@
         mPackageName = appInfo.packageName;
 
         final PackageManager pm = context.getPackageManager();
-        final CharSequence label = appInfo.loadSafeLabel(pm);
+        final CharSequence label = appInfo.loadSafeLabel(pm,
+                PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+                        | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
         final CharSequence message = context.getString(
                 R.string.unsupported_display_size_message, label);
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5a91ea9..93ef315 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -524,16 +524,13 @@
     // SCO audio state is active or starting due to a request from AudioManager API
     private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
     // SCO audio deactivation request waiting for headset service to connect
-    private static final int SCO_STATE_DEACTIVATE_REQ = 5;
+    private static final int SCO_STATE_DEACTIVATE_REQ = 4;
     // SCO audio deactivation in progress, waiting for Bluetooth audio intent
-    private static final int SCO_STATE_DEACTIVATING = 6;
+    private static final int SCO_STATE_DEACTIVATING = 5;
 
     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
     // in call audio)
     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
-    // Deactivation request for all SCO connections (initiated by audio mode change)
-    // waiting for headset service to connect
-    private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
 
     // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
     // originated from an app targeting an API version before JB MR2 and raw audio after that.
@@ -3593,33 +3590,19 @@
         return result;
     }
 
+    /**
+     * Disconnect all SCO connections started by {@link AudioManager} except those started by
+     * {@param exceptPid}
+     *
+     * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
+     */
     private void disconnectBluetoothSco(int exceptPid) {
         synchronized(mScoClients) {
             checkScoAudioState();
-            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
-                    mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
-                if (mBluetoothHeadsetDevice != null) {
-                    if (mBluetoothHeadset != null) {
-                        boolean status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, SCO_MODE_RAW)
-                                || disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL)
-                                || disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, SCO_MODE_VR);
-                        if (status) {
-                            mScoAudioState = SCO_STATE_DEACTIVATING;
-                        } else {
-                            clearAllScoClients(exceptPid, false);
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                        }
-                    } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
-                            getBluetoothHeadset()) {
-                        mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
-                    }
-                }
-            } else {
-                clearAllScoClients(exceptPid, true);
+            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
+                return;
             }
+            clearAllScoClients(exceptPid, true);
         }
     }
 
@@ -3785,8 +3768,7 @@
                     checkScoAudioState();
                     // Continue pending action if any
                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
-                            mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
-                            mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+                            mScoAudioState == SCO_STATE_DEACTIVATE_REQ) {
                         boolean status = false;
                         if (mBluetoothHeadsetDevice != null) {
                             switch (mScoAudioState) {
@@ -3804,17 +3786,6 @@
                                         mScoAudioState = SCO_STATE_DEACTIVATING;
                                     }
                                     break;
-                                case SCO_STATE_DEACTIVATE_EXT_REQ:
-                                    status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                            mBluetoothHeadsetDevice, SCO_MODE_RAW) ||
-                                            disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                            mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL) ||
-                                            disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                            mBluetoothHeadsetDevice, SCO_MODE_VR);
-                                    if (status) {
-                                        mScoAudioState = SCO_STATE_DEACTIVATING;
-                                    }
-                                    break;
                             }
                         }
                         if (!status) {
@@ -6397,8 +6368,7 @@
                         case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                             scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                             if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
-                                mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
-                                mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
+                                mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                                 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                             }
                             setBluetoothScoOn(true);
@@ -6422,8 +6392,7 @@
                             break;
                         case BluetoothHeadset.STATE_AUDIO_CONNECTING:
                             if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
-                                mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
-                                mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
+                                mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                                 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                             }
                         default:
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8562572..a6dfec7 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -680,7 +680,7 @@
          * @throws SecurityException if the permission check fails
          */
         private void enforceChangeOverlayPackagesPermission(@NonNull final String message) {
-            getContext().enforceCallingOrSelfPermission(
+            getContext().enforceCallingPermission(
                     android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, message);
         }
 
@@ -691,8 +691,7 @@
          * @throws SecurityException if the permission check fails
          */
         private void enforceDumpPermission(@NonNull final String message) {
-            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
-                    message);
+            getContext().enforceCallingPermission(android.Manifest.permission.DUMP, message);
         }
     };
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 12dfd9a..72d1ae9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8804,7 +8804,7 @@
             // equal to the version on the /data partition. Throw an exception and use
             // the application already installed on the /data partition.
             throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
-                    + pkg.codePath + " ignored: updated version " + disabledPkgSetting.versionCode
+                    + pkg.codePath + " ignored: updated version " + pkgSetting.versionCode
                     + " better than this " + pkg.getLongVersionCode());
         }
 
@@ -11194,8 +11194,23 @@
                                 mSettings.getPackageLPr(pkg.packageName),
                                 "previous package state not present");
 
+                        // previousPkg.pkg may be null: the package will be not be scanned if the
+                        // package manager knows there is a newer version on /data.
+                        // TODO[b/79435695]: Find a better way to keep track of the "static"
+                        // property for RROs instead of having to parse packages on /system
+                        PackageParser.Package ppkg = previousPkg.pkg;
+                        if (ppkg == null) {
+                            try {
+                                final PackageParser pp = new PackageParser();
+                                ppkg = pp.parsePackage(previousPkg.codePath,
+                                        parseFlags | PackageParser.PARSE_IS_SYSTEM_DIR);
+                            } catch (PackageParserException e) {
+                                Slog.w(TAG, "failed to parse " + previousPkg.codePath, e);
+                            }
+                        }
+
                         // Static overlays cannot be updated.
-                        if (previousPkg.pkg.mOverlayIsStatic) {
+                        if (ppkg != null && ppkg.mOverlayIsStatic) {
                             throw new PackageManagerException("Overlay " + pkg.packageName +
                                     " is static and cannot be upgraded.");
                         // Non-static overlays cannot be converted to static overlays.