Merge "Remove unused @hide APIs from SmsManager."
diff --git a/Android.bp b/Android.bp
index 6fc233c..632ef1c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,14 @@
 }
 
 filegroup {
+    name: "framework-mime-sources",
+    srcs: [
+        "mime/java/**/*.java",
+    ],
+    path: "mime/java",
+}
+
+filegroup {
     name: "framework-opengl-sources",
     srcs: [
         "opengl/java/**/*.java",
@@ -176,6 +184,7 @@
         ":framework-mca-effect-sources",
         ":framework-mca-filterfw-sources",
         ":framework-mca-filterpacks-sources",
+        ":framework-mime-sources",
         ":framework-opengl-sources",
         ":framework-rs-sources",
         ":framework-sax-sources",
@@ -305,7 +314,7 @@
 
     exclude_srcs: [
         // See comment on framework-atb-backward-compatibility module below
-        "core/java/android/content/pm/AndroidTestBaseUpdater.java",
+        "core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java",
     ],
 
     sdk_version: "core_platform",
@@ -316,7 +325,10 @@
 
     jarjar_rules: ":framework-jarjar-rules",
 
-    static_libs: ["framework-internal-utils"],
+    static_libs: [
+        "framework-internal-utils",
+        "mimemap",
+    ],
 
     dxflags: [
         "--core-library",
@@ -435,7 +447,7 @@
     name: "framework-atb-backward-compatibility",
     installable: true,
     srcs: [
-        "core/java/android/content/pm/AndroidTestBaseUpdater.java",
+        "core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java",
     ],
 }
 
diff --git a/apex/jobscheduler/README_js-mainline.md b/apex/jobscheduler/README_js-mainline.md
index c1ad666..ea20e3e 100644
--- a/apex/jobscheduler/README_js-mainline.md
+++ b/apex/jobscheduler/README_js-mainline.md
@@ -1,23 +1,11 @@
 # Making Job Scheduler into a Mainline Module
 
-## TODOs
-
-See also:
-- http://go/moving-js-code-for-mainline
-- http://go/jobscheduler-code-dependencies-2019-07
-
-- [ ] Move this into `frameworks/apex/jobscheduler/...`. Currently it's in `frameworks/base/apex/...`
-because `frameworks/apex/` is not a part of any git projects. (and also working on multiple
-projects is a pain.)
-
 ## Current structure
 
 - JS service side classes are put in `jobscheduler-service.jar`.
 It's *not* included in services.jar, and instead it's put in the system server classpath,
 which currently looks like the following:
-`SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/jobscheduler-service.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/com.android.location.provider.jar`
-
-  (Note `jobscheduler-service.jar` will be put at the end in http://ag/9128109)
+`SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/com.android.location.provider.jar:/system/framework/jobscheduler-service.jar`
 
   `SYSTEMSERVERCLASSPATH` is generated from `PRODUCT_SYSTEM_SERVER_JARS`.
 
@@ -29,10 +17,4 @@
   `framework.jar` merging the two jar files, and this jar file is what's
   put on the device and loaded by Zygote.
 
-
-This is *not* the final design. From a gerrit comment on http://ag/9145619:
-
-> This CL is just the first step, and the current state isn't not really the final form. For now we just want to have two separate jars, which makes it easier for us to analyze dependencies between them, and I wanted to minimize the change to the rest of the system. So, for example, zygote will still only have "framework.jar" in its classpath, instead of the two jars for now.
-> But yes, eventually, we won't even be able to have the monolithic "framework.jar" file because of mainline, so we need to figure out how to build the system without creating it. At that point zygote will have the two separate jar files in its classpath.
-> When we reach that point, we should revisit the naming of it, and yes, maybe the simple "framework.jar" is a good option.
-> But again, for now, I want to make this change as transparent as possible to the rest of the world.
+The current structure is *not* the final design.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index c2bdb6c..2f5f555 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -1157,22 +1157,14 @@
         private void removeAll(Predicate<JobStatus> predicate) {
             for (int jobSetIndex = mJobs.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
                 final ArraySet<JobStatus> jobs = mJobs.valueAt(jobSetIndex);
-                for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
-                    if (predicate.test(jobs.valueAt(jobIndex))) {
-                        jobs.removeAt(jobIndex);
-                    }
-                }
+                jobs.removeIf(predicate);
                 if (jobs.size() == 0) {
                     mJobs.removeAt(jobSetIndex);
                 }
             }
             for (int jobSetIndex = mJobsPerSourceUid.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
                 final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(jobSetIndex);
-                for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
-                    if (predicate.test(jobs.valueAt(jobIndex))) {
-                        jobs.removeAt(jobIndex);
-                    }
-                }
+                jobs.removeIf(predicate);
                 if (jobs.size() == 0) {
                     mJobsPerSourceUid.removeAt(jobSetIndex);
                 }
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
new file mode 100644
index 0000000..786e8b0
--- /dev/null
+++ b/apex/statsd/service/Android.bp
@@ -0,0 +1,16 @@
+// Statsd Service jar, which will eventually be put in the statsd mainline apex.
+// statsd-service needs to be added to PRODUCT_SYSTEM_SERVER_JARS.
+// This jar will contain StatsCompanionService
+java_library {
+    name: "statsd-service",
+    installable: true,
+
+    srcs: [
+        "java/**/*.java",
+    ],
+
+    libs: [
+        "framework",
+        "services.core",
+    ],
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
similarity index 98%
rename from services/core/java/com/android/server/stats/StatsCompanionService.java
rename to apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 67830a9..1d3ac6a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -24,11 +24,10 @@
 import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
 import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
 import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
 import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
 import android.annotation.NonNull;
@@ -1193,48 +1192,13 @@
             e.writeLong(memoryStat.rssInBytes);
             e.writeLong(memoryStat.cacheInBytes);
             e.writeLong(memoryStat.swapInBytes);
-            e.writeLong(0);  // unused
-            e.writeLong(memoryStat.startTimeNanos);
-            e.writeInt(anonAndSwapInKilobytes(memoryStat));
+            e.writeLong(-1);  // unused
+            e.writeLong(-1);  // unused
+            e.writeInt(-1);  // unsed
             pulledData.add(e);
         }
     }
 
-    private void pullNativeProcessMemoryState(
-            int tagId, long elapsedNanos, long wallClockNanos,
-            List<StatsLogEventWrapper> pulledData) {
-        int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
-        for (int pid : pids) {
-            String processName = readCmdlineFromProcfs(pid);
-            MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
-            if (memoryStat == null) {
-                continue;
-            }
-            int uid = getUidForPid(pid);
-            // Sometimes we get here a process that is not included in the whitelist. It comes
-            // from forking the zygote for an app. We can ignore that sample because this process
-            // is collected by ProcessMemoryState.
-            if (isAppUid(uid)) {
-                continue;
-            }
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeInt(uid);
-            e.writeString(processName);
-            e.writeLong(memoryStat.pgfault);
-            e.writeLong(memoryStat.pgmajfault);
-            e.writeLong(memoryStat.rssInBytes);
-            e.writeLong(0);  // unused
-            e.writeLong(memoryStat.startTimeNanos);
-            e.writeLong(memoryStat.swapInBytes);
-            e.writeInt(anonAndSwapInKilobytes(memoryStat));
-            pulledData.add(e);
-        }
-    }
-
-    private static int anonAndSwapInKilobytes(MemoryStat memoryStat) {
-        return (int) ((memoryStat.anonRssInBytes + memoryStat.swapInBytes) / 1024);
-    }
-
     private void pullProcessMemoryHighWaterMark(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
@@ -2405,10 +2369,6 @@
                 pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
-            case StatsLog.NATIVE_PROCESS_MEMORY_STATE: {
-                pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
-                break;
-            }
             case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
                 pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/api/current.txt b/api/current.txt
index fc7685d..44cb997 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44291,6 +44291,7 @@
     field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
     field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
     field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+    field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
     field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
diff --git a/api/system-current.txt b/api/system-current.txt
index 279d2c8..e92dc89 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5901,7 +5901,8 @@
     field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
     field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
     field public static final String NAMESPACE_SCHEDULER = "scheduler";
-    field public static final String NAMESPACE_STORAGE = "storage";
+    field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
+    field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
     field public static final String NAMESPACE_TELEPHONY = "telephony";
     field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
@@ -7858,6 +7859,8 @@
     method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9b00a42..ee631be 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2890,6 +2890,11 @@
     method public static void setMinMatchForTest(int);
   }
 
+  public class PhoneStateListener {
+    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 268435456; // 0x10000000
+    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 536870912; // 0x20000000
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
     method public void setCdmaSystemAndNetworkId(int, int);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b71a86b..6249de3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -378,7 +378,6 @@
         PowerProfile power_profile = 10033;
         ProcStatsPkgProc proc_stats_pkg_proc = 10034;
         ProcessCpuTime process_cpu_time = 10035;
-        NativeProcessMemoryState native_process_memory_state = 10036;
         CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
         OnDevicePowerMeasurement on_device_power_measurement = 10038;
         DeviceCalculatedPowerUse device_calculated_power_use = 10039;
@@ -412,6 +411,8 @@
     // DO NOT USE field numbers above 100,000 in AOSP.
     // Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
     // Field numbers 200,000 and above are reserved for future use; do not use them at all.
+
+    reserved 10036;
 }
 
 /**
@@ -4019,8 +4020,8 @@
     optional int64 page_major_fault = 5;
 
     // RSS
-    // Value is read from /proc/PID/status. Or from memory.stat, field
-    // total_rss if per-app memory cgroups are enabled.
+    // Value is read from memory.stat, field total_rss if per-app memory
+    // cgroups are enabled. Otherwise, value from /proc/pid/stat.
     optional int64 rss_in_bytes = 6;
 
     // CACHE
@@ -4030,56 +4031,17 @@
 
     // SWAP
     // Value is read from memory.stat, field total_swap if per-app memory
-    // cgroups are enabled. Otherwise, VmSwap from /proc/PID/status.
+    // cgroups are enabled. Otherwise, 0.
     optional int64 swap_in_bytes = 8;
 
-    // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
+    // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1.
     optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
 
-    // Elapsed real time when the process started.
-    // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
-    optional int64 start_time_nanos = 10;
+    // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+    optional int64 start_time_nanos = 10 [deprecated = true];
 
-    // Anonymous page size plus swap size. Values are read from /proc/PID/status.
-    optional int32 anon_rss_and_swap_in_kilobytes = 11;
-}
-
-/*
- * Logs the memory stats for a native process (from procfs).
- *
- * Pulled from StatsCompanionService for selected native processes.
- */
-message NativeProcessMemoryState {
-    // The uid if available. -1 means not available.
-    optional int32 uid = 1 [(is_uid) = true];
-
-    // The process name.
-    // Value read from /proc/PID/cmdline.
-    optional string process_name = 2;
-
-    // # of page-faults
-    optional int64 page_fault = 3;
-
-    // # of major page-faults
-    optional int64 page_major_fault = 4;
-
-    // RSS
-    // Value read from /proc/PID/status.
-    optional int64 rss_in_bytes = 5;
-
-    // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
-    optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
-
-    // Elapsed real time when the process started.
-    // Value is read from /proc/PID/stat, field 22.
-    optional int64 start_time_nanos = 7;
-
-    // SWAP
-    // Value read from /proc/PID/status, field VmSwap.
-    optional int64 swap_in_bytes = 8;
-
-    // Anonymous page size plus swap size. Values are read from /proc/PID/status.
-    optional int32 anon_rss_and_swap_in_kilobytes = 9;
+    // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+    optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true];
 }
 
 /*
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 7a183a3..43e33f5 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -145,16 +145,11 @@
          {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
-         {.additiveFields = {4, 5, 6, 7, 8, 9},
+         {.additiveFields = {4, 5, 6, 7, 8},
           .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
-        // native_process_memory_state
-        {android::util::NATIVE_PROCESS_MEMORY_STATE,
-         {.additiveFields = {3, 4, 5, 6, 8},
-          .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         // process_memory_high_water_mark
         {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
-         {.additiveFields = {3},
-          .puller =
+         {.puller =
                   new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // process_memory_snapshot
         {android::util::PROCESS_MEMORY_SNAPSHOT,
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index e0f7d86..b37ee74 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -65,6 +65,14 @@
     private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
     private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
+    /**
+     * Change the system dialer package name if a package name was specified,
+     * Example: adb shell telecom set-system-dialer <PACKAGE>
+     *
+     * Restore it to the default if if argument is "default" or no argument is passed.
+     * Example: adb shell telecom set-system-dialer default
+     */
+    private static final String COMMAND_SET_SYSTEM_DIALER = "set-system-dialer";
     private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer";
     private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers";
     private static final String COMMAND_SET_SIM_COUNT = "set-sim-count";
@@ -193,6 +201,9 @@
             case COMMAND_GET_DEFAULT_DIALER:
                 runGetDefaultDialer();
                 break;
+            case COMMAND_SET_SYSTEM_DIALER:
+                runSetSystemDialer();
+                break;
             case COMMAND_GET_SYSTEM_DIALER:
                 runGetSystemDialer();
                 break;
@@ -297,6 +308,12 @@
         System.out.println("Success - " + packageName + " set as override default dialer.");
     }
 
+    private void runSetSystemDialer() throws RemoteException {
+        final String packageName = nextArg();
+        mTelecomService.setSystemDialerPackage(packageName.equals("default") ? null : packageName);
+        System.out.println("Success - " + packageName + " set as override system dialer.");
+    }
+
     private void runGetDefaultDialer() throws RemoteException {
         System.out.println(mTelecomService.getDefaultDialerPackage());
     }
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 8d91144..5698dfe 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -877,7 +877,7 @@
 android.content.pm.ActivityInfo$1
 android.content.pm.ActivityInfo$WindowLayout
 android.content.pm.ActivityInfo
-android.content.pm.AndroidHidlUpdater
+android.content.pm.parsing.library.AndroidHidlUpdater
 android.content.pm.ApplicationInfo$1
 android.content.pm.ApplicationInfo
 android.content.pm.BaseParceledListSlice
@@ -921,10 +921,10 @@
 android.content.pm.LauncherApps
 android.content.pm.ModuleInfo$1
 android.content.pm.ModuleInfo
-android.content.pm.OrgApacheHttpLegacyUpdater
-android.content.pm.PackageBackwardCompatibility$AndroidTestRunnerSplitUpdater
-android.content.pm.PackageBackwardCompatibility$RemoveUnnecessaryAndroidTestBaseLibrary
-android.content.pm.PackageBackwardCompatibility
+android.content.pm.parsing.library.OrgApacheHttpLegacyUpdater
+android.content.pm.parsing.library.PackageBackwardCompatibility$AndroidTestRunnerSplitUpdater
+android.content.pm.parsing.library.PackageBackwardCompatibility$RemoveUnnecessaryAndroidTestBaseLibrary
+android.content.pm.parsing.library.PackageBackwardCompatibility
 android.content.pm.PackageInfo$1
 android.content.pm.PackageInfo
 android.content.pm.PackageInstaller$Session
@@ -959,7 +959,7 @@
 android.content.pm.PackageParser$SigningDetails
 android.content.pm.PackageParser$SplitNameComparator
 android.content.pm.PackageParser
-android.content.pm.PackageSharedLibraryUpdater
+android.content.pm.parsing.library.PackageSharedLibraryUpdater
 android.content.pm.PackageStats$1
 android.content.pm.PackageStats
 android.content.pm.PackageUserState
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index cb99a3a..7f597fe 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -188,57 +188,6 @@
     final ArrayMap<OnUidImportanceListener, UidObserver> mImportanceListeners = new ArrayMap<>();
 
     /**
-     * Defines acceptable types of bugreports.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "BUGREPORT_OPTION_" }, value = {
-            BUGREPORT_OPTION_FULL,
-            BUGREPORT_OPTION_INTERACTIVE,
-            BUGREPORT_OPTION_REMOTE,
-            BUGREPORT_OPTION_WEAR,
-            BUGREPORT_OPTION_TELEPHONY,
-            BUGREPORT_OPTION_WIFI
-    })
-    public @interface BugreportMode {}
-    /**
-     * Takes a bugreport without user interference (and hence causing less
-     * interference to the system), but includes all sections.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_FULL = 0;
-    /**
-     * Allows user to monitor progress and enter additional data; might not include all
-     * sections.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_INTERACTIVE = 1;
-    /**
-     * Takes a bugreport requested remotely by administrator of the Device Owner app,
-     * not the device's user.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_REMOTE = 2;
-    /**
-     * Takes a bugreport on a wearable device.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_WEAR = 3;
-
-    /**
-     * Takes a lightweight version of bugreport that only includes a few, urgent sections
-     * used to report telephony bugs.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_TELEPHONY = 4;
-
-    /**
-     * Takes a lightweight bugreport that only includes a few sections related to Wifi.
-     * @hide
-     */
-    public static final int BUGREPORT_OPTION_WIFI = 5;
-
-    /**
      * <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
      * <meta-data>}</a> name for a 'home' Activity that declares a package that is to be
      * uninstalled in lieu of the declaring one.  The package named here must be
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8765760..b873be3 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -153,4 +153,11 @@
      * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
      */
     protected abstract DevicePolicyCache getDevicePolicyCache();
+
+    /**
+     * @return cached version of device state related to DPM that can be accessed without risking
+     * deadlocks.
+     * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
+     */
+    protected abstract DeviceStateCache getDeviceStateCache();
 }
diff --git a/core/java/android/app/admin/DeviceStateCache.java b/core/java/android/app/admin/DeviceStateCache.java
new file mode 100644
index 0000000..7619aa2
--- /dev/null
+++ b/core/java/android/app/admin/DeviceStateCache.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import com.android.server.LocalServices;
+
+/**
+ * Stores a copy of the set of device state maintained by {@link DevicePolicyManager} which
+ * is not directly related to admin policies. This lives in its own class so that the state
+ * can be accessed from any place without risking dead locks.
+ *
+ * @hide
+ */
+public abstract class DeviceStateCache {
+    protected DeviceStateCache() {
+    }
+
+    /**
+     * @return the instance.
+     */
+    public static DeviceStateCache getInstance() {
+        final DevicePolicyManagerInternal dpmi =
+                LocalServices.getService(DevicePolicyManagerInternal.class);
+        return (dpmi != null) ? dpmi.getDeviceStateCache() : EmptyDeviceStateCache.INSTANCE;
+    }
+
+    /**
+     * See {@link DevicePolicyManager#isDeviceProvisioned}
+     */
+    public abstract boolean isDeviceProvisioned();
+
+    /**
+     * Empty implementation.
+     */
+    private static class EmptyDeviceStateCache extends DeviceStateCache {
+        private static final EmptyDeviceStateCache INSTANCE = new EmptyDeviceStateCache();
+
+        @Override
+        public boolean isDeviceProvisioned() {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 415c242..29f243f7 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1379,7 +1379,8 @@
             this.minHeight = minHeight;
         }
 
-        WindowLayout(Parcel source) {
+        /** @hide */
+        public WindowLayout(Parcel source) {
             width = source.readInt();
             widthFraction = source.readFloat();
             height = source.readInt();
diff --git a/core/java/android/content/pm/AndroidHidlUpdater.java b/core/java/android/content/pm/AndroidHidlUpdater.java
deleted file mode 100644
index d0657e5..0000000
--- a/core/java/android/content/pm/AndroidHidlUpdater.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm;
-
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-
-import android.content.pm.PackageParser.Package;
-import android.os.Build;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
- * and android.hidl.manager-V1.0-java libraries are included by default.
- *
- * @hide
- */
-@VisibleForTesting
-public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
-
-    @Override
-    public void updatePackage(Package pkg) {
-        ApplicationInfo info = pkg.applicationInfo;
-
-        // This was the default <= P and is maintained for backwards compatibility.
-        boolean isLegacy = info.targetSdkVersion <= Build.VERSION_CODES.P;
-        // Only system apps use these libraries
-        boolean isSystem = info.isSystemApp() || info.isUpdatedSystemApp();
-
-        if (isLegacy && isSystem) {
-            prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE);
-            prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER);
-        } else {
-            removeLibrary(pkg, ANDROID_HIDL_BASE);
-            removeLibrary(pkg, ANDROID_HIDL_MANAGER);
-        }
-    }
-}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d6fb28f..aa0002d 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -373,8 +373,9 @@
 
     /**
      * Whether the overlay is static, meaning it cannot be enabled/disabled at runtime.
+     * @hide
      */
-    boolean mOverlayIsStatic;
+    public boolean mOverlayIsStatic;
 
     /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index f28b85c..7465498 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,6 +28,8 @@
 import android.content.pm.PackageManager.ComponentInfoFlags;
 import android.content.pm.PackageManager.PackageInfoFlags;
 import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
@@ -315,7 +317,7 @@
      * @param installed the new installed state
      * @return true if the installed state changed as a result
      */
-    public abstract boolean setInstalled(PackageParser.Package pkg,
+    public abstract boolean setInstalled(AndroidPackage pkg,
             @UserIdInt int userId, boolean installed);
 
     /**
@@ -394,7 +396,7 @@
      * Returns whether or not the given package represents a legacy system application released
      * prior to runtime permissions.
      */
-    public abstract boolean isLegacySystemApp(PackageParser.Package pkg);
+    public abstract boolean isLegacySystemApp(AndroidPackage pkg);
 
     /**
      * Get all overlay packages for a user.
@@ -486,13 +488,17 @@
     /**
      * Returns a package object for the given package name.
      */
-    public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
+    public abstract @Nullable AndroidPackage getPackage(@NonNull String packageName);
+
+    // TODO(b/135203078): PackageSetting can't be referenced directly. Should move to a server side
+    //  internal PM which is aware of PS.
+    public abstract @Nullable Object getPackageSetting(String packageName);
 
     /**
      * Returns a package for the given UID. If the UID is part of a shared user ID, one
      * of the packages will be chosen to be returned.
      */
-    public abstract @Nullable PackageParser.Package getPackage(int uid);
+    public abstract @Nullable AndroidPackage getPackage(int uid);
 
     /**
      * Returns a list without a change observer.
@@ -523,17 +529,19 @@
      */
     public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
 
+    // TODO(b/135203078): PackageSetting can't be referenced directly
     /**
      * Returns a package object for the disabled system package name.
      */
-    public abstract @Nullable PackageParser.Package getDisabledSystemPackage(
-            @NonNull String packageName);
+    public abstract @Nullable Object getDisabledSystemPackage(@NonNull String packageName);
 
     /**
      * Returns the package name for the disabled system package.
      *
      * This is equivalent to
-     * {@link #getDisabledSystemPackage(String)}.{@link PackageParser.Package#packageName}
+     * {@link #getDisabledSystemPackage(String)}
+     *     .{@link com.android.server.pm.PackageSetting#pkg}
+     *     .{@link AndroidPackage#getPackageName()}
      */
     public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
 
@@ -567,7 +575,7 @@
      * @see #canAccessInstantApps
      */
     public abstract boolean filterAppAccess(
-            @NonNull PackageParser.Package pkg, int callingUid, int userId);
+            @NonNull AndroidPackage pkg, int callingUid, int userId);
 
     /**
      * Returns whether or not access to the application should be filtered.
@@ -641,7 +649,8 @@
             throws IOException;
 
     /** Returns {@code true} if the specified component is enabled and matches the given flags. */
-    public abstract boolean isEnabledAndMatches(@NonNull ComponentInfo info, int flags, int userId);
+    public abstract boolean isEnabledAndMatches(
+            @NonNull ComponentParseUtils.ParsedComponent component, int flags, int userId);
 
     /** Returns {@code true} if the given user requires extra badging for icons. */
     public abstract boolean userNeedsBadging(int userId);
@@ -652,14 +661,14 @@
      *
      * @param actionLocked action to be performed
      */
-    public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+    public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
 
     /**
      * Perform the given action for each installed package for a user.
      * Note that packages lock will be held while performin the actions.
      */
     public abstract void forEachInstalledPackage(
-            @NonNull Consumer<PackageParser.Package> actionLocked, @UserIdInt int userId);
+            @NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
 
     /** Returns the list of enabled components */
     public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
@@ -793,7 +802,7 @@
      * Otherwise, {@code false}.
      */
     public abstract boolean isCallerInstallerOfRecord(
-            @NonNull PackageParser.Package pkg, int callingUid);
+            @NonNull AndroidPackage pkg, int callingUid);
 
     /** Returns whether or not default runtime permissions are granted for the given user */
     public abstract boolean areDefaultRuntimePermissionsGranted(@UserIdInt int userId);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b157fa..2ded5dc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -57,6 +57,12 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageParserCacheHelper.ReadHelper;
 import android.content.pm.PackageParserCacheHelper.WriteHelper;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.ParsedPackage;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
@@ -67,7 +73,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
@@ -156,21 +161,22 @@
  * @hide
  */
 public class PackageParser {
-    private static final boolean DEBUG_JAR = false;
-    private static final boolean DEBUG_PARSER = false;
-    private static final boolean DEBUG_BACKUP = false;
-    private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
-    private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
+
+    public static final boolean DEBUG_JAR = false;
+    public static final boolean DEBUG_PARSER = false;
+    public static final boolean DEBUG_BACKUP = false;
+    public static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
+    public static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
 
     private static final String PROPERTY_CHILD_PACKAGES_ENABLED =
             "persist.sys.child_packages_enabled";
 
-    private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
+    public static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
             SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
 
-    private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
-    private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
-    private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
+    public static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f;
+    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
+    public static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
 
     private static final int DEFAULT_MIN_SDK_VERSION = 1;
     private static final int DEFAULT_TARGET_SDK_VERSION = 0;
@@ -182,37 +188,38 @@
     public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
 
     /** Path prefix for apps on expanded storage */
-    private static final String MNT_EXPAND = "/mnt/expand/";
+    public static final String MNT_EXPAND = "/mnt/expand/";
 
-    private static final String TAG_MANIFEST = "manifest";
-    private static final String TAG_APPLICATION = "application";
-    private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
-    private static final String TAG_OVERLAY = "overlay";
-    private static final String TAG_KEY_SETS = "key-sets";
-    private static final String TAG_PERMISSION_GROUP = "permission-group";
-    private static final String TAG_PERMISSION = "permission";
-    private static final String TAG_PERMISSION_TREE = "permission-tree";
-    private static final String TAG_USES_PERMISSION = "uses-permission";
-    private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
-    private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
-    private static final String TAG_USES_CONFIGURATION = "uses-configuration";
-    private static final String TAG_USES_FEATURE = "uses-feature";
-    private static final String TAG_FEATURE_GROUP = "feature-group";
-    private static final String TAG_USES_SDK = "uses-sdk";
-    private static final String TAG_SUPPORT_SCREENS = "supports-screens";
-    private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
-    private static final String TAG_INSTRUMENTATION = "instrumentation";
-    private static final String TAG_ORIGINAL_PACKAGE = "original-package";
-    private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
-    private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
-    private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
-    private static final String TAG_SUPPORTS_INPUT = "supports-input";
-    private static final String TAG_EAT_COMMENT = "eat-comment";
-    private static final String TAG_PACKAGE = "package";
-    private static final String TAG_RESTRICT_UPDATE = "restrict-update";
-    private static final String TAG_USES_SPLIT = "uses-split";
+    public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+    public static final String TAG_APPLICATION = "application";
+    public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+    public static final String TAG_EAT_COMMENT = "eat-comment";
+    public static final String TAG_FEATURE_GROUP = "feature-group";
+    public static final String TAG_INSTRUMENTATION = "instrumentation";
+    public static final String TAG_KEY_SETS = "key-sets";
+    public static final String TAG_MANIFEST = "manifest";
+    public static final String TAG_ORIGINAL_PACKAGE = "original-package";
+    public static final String TAG_OVERLAY = "overlay";
+    public static final String TAG_PACKAGE = "package";
+    public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+    public static final String TAG_PERMISSION = "permission";
+    public static final String TAG_PERMISSION_GROUP = "permission-group";
+    public static final String TAG_PERMISSION_TREE = "permission-tree";
+    public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+    public static final String TAG_QUERIES = "queries";
+    public static final String TAG_RESTRICT_UPDATE = "restrict-update";
+    public static final String TAG_SUPPORT_SCREENS = "supports-screens";
+    public static final String TAG_SUPPORTS_INPUT = "supports-input";
+    public static final String TAG_USES_CONFIGURATION = "uses-configuration";
+    public static final String TAG_USES_FEATURE = "uses-feature";
+    public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+    public static final String TAG_USES_PERMISSION = "uses-permission";
+    public static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+    public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+    public static final String TAG_USES_SDK = "uses-sdk";
+    public static final String TAG_USES_SPLIT = "uses-split";
 
-    private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+    public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
 
     /**
      * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
@@ -222,25 +229,25 @@
             ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
 
     // These are the tags supported by child packages
-    private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
+    public static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
     static {
         CHILD_PACKAGE_TAGS.add(TAG_APPLICATION);
-        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION);
-        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M);
-        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23);
+        CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+        CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
+        CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
+        CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
         CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION);
         CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE);
-        CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
-        CHILD_PACKAGE_TAGS.add(TAG_USES_SDK);
-        CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
-        CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
         CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE);
-        CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS);
-        CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
-        CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M);
+        CHILD_PACKAGE_TAGS.add(TAG_USES_SDK);
     }
 
-    private static final boolean LOG_UNSAFE_BROADCASTS = false;
+    public static final boolean LOG_UNSAFE_BROADCASTS = false;
 
     /**
      * Total number of packages that were read from the cache.  We use it only for logging.
@@ -248,7 +255,7 @@
     public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
 
     // Set of broadcast actions that are safe for manifest receivers
-    private static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
+    public static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
     static {
         SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
     }
@@ -295,26 +302,29 @@
      * @deprecated callers should move to explicitly passing around source path.
      */
     @Deprecated
-    private String mArchiveSourcePath;
+    public String mArchiveSourcePath;
 
-    private String[] mSeparateProcesses;
+    public String[] mSeparateProcesses;
     private boolean mOnlyCoreApps;
     private DisplayMetrics mMetrics;
     @UnsupportedAppUsage
-    private Callback mCallback;
+    public Callback mCallback;
     private File mCacheDir;
 
-    private static final int SDK_VERSION = Build.VERSION.SDK_INT;
-    private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+    public static final int SDK_VERSION = Build.VERSION.SDK_INT;
+    public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
 
-    private int mParseError = PackageManager.INSTALL_SUCCEEDED;
+    public int mParseError = PackageManager.INSTALL_SUCCEEDED;
 
-    private static boolean sCompatibilityModeEnabled = true;
-    private static boolean sUseRoundIcon = false;
+    public ThreadLocal<ApkParseUtils.ParseResult> mSharedResult
+            = ThreadLocal.withInitial(ApkParseUtils.ParseResult::new);
 
-    private static final int PARSE_DEFAULT_INSTALL_LOCATION =
+    public static boolean sCompatibilityModeEnabled = true;
+    public static boolean sUseRoundIcon = false;
+
+    public static final int PARSE_DEFAULT_INSTALL_LOCATION =
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
-    private static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
+    public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
 
     static class ParsePackageItemArgs {
         final Package owner;
@@ -536,7 +546,7 @@
      *  the DTD.  Otherwise, we try to get as much from the package as we
      *  can without failing.  This should normally be set to false, to
      *  support extensions to the DTD in future versions. */
-    private static final boolean RIGID_PARSER = false;
+    public static final boolean RIGID_PARSER = false;
 
     private static final String TAG = "PackageParser";
 
@@ -897,7 +907,7 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ParseFlags {}
 
-    private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
+    public static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
     /**
      * Used to sort a set of APKs based on their split names, always placing the
@@ -1043,7 +1053,7 @@
      * and unique split names.
      * <p>
      * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #collectCertificates(Package, int)}.
+     * must be done separately in {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
      *
      * If {@code useCaches} is true, the package parser might return a cached
      * result from a previous parse of the same {@code packageFile} with the same
@@ -1051,21 +1061,54 @@
      * has changed since the last parse, it's up to callers to do so.
      *
      * @see #parsePackageLite(File, int)
+     * @deprecated use {@link #parseParsedPackage(File, int, boolean)}
      */
     @UnsupportedAppUsage
+    @Deprecated
     public Package parsePackage(File packageFile, int flags, boolean useCaches)
             throws PackageParserException {
-        Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
+        if (packageFile.isDirectory()) {
+            return parseClusterPackage(packageFile, flags);
+        } else {
+            return parseMonolithicPackage(packageFile, flags);
+        }
+    }
+
+    /**
+     * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
+     * @deprecated use {@link #parseParsedPackage(File, int, boolean)}
+     */
+    @UnsupportedAppUsage
+    @Deprecated
+    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
+        return parsePackage(packageFile, flags, false /* useCaches */);
+    }
+
+    /**
+     * Updated method which returns {@link ParsedPackage}, the current representation of a
+     * package parsed from disk.
+     *
+     * @see #parsePackage(File, int, boolean)
+     */
+    public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches)
+            throws PackageParserException {
+        ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null;
         if (parsed != null) {
             return parsed;
         }
 
         long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
-        if (packageFile.isDirectory()) {
-            parsed = parseClusterPackage(packageFile, flags);
-        } else {
-            parsed = parseMonolithicPackage(packageFile, flags);
-        }
+        ApkParseUtils.ParseInput parseInput = mSharedResult.get().reset();
+        parsed = ApkParseUtils.parsePackage(
+                parseInput,
+                mSeparateProcesses,
+                mCallback,
+                mMetrics,
+                mOnlyCoreApps,
+                packageFile,
+                flags
+        )
+                .hideAsParsed();
 
         long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
         cacheResult(packageFile, flags, parsed);
@@ -1077,19 +1120,12 @@
                         + "ms, update_cache=" + cacheTime + " ms");
             }
         }
+
         return parsed;
     }
 
     /**
-     * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
-     */
-    @UnsupportedAppUsage
-    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
-        return parsePackage(packageFile, flags, false /* useCaches */);
-    }
-
-    /**
-     * Returns the cache key for a specificied {@code packageFile} and {@code flags}.
+     * Returns the cache key for a specified {@code packageFile} and {@code flags}.
      */
     private String getCacheKey(File packageFile, int flags) {
         StringBuilder sb = new StringBuilder(packageFile.getName());
@@ -1100,13 +1136,13 @@
     }
 
     @VisibleForTesting
-    protected Package fromCacheEntry(byte[] bytes) {
+    protected ParsedPackage fromCacheEntry(byte[] bytes) {
         return fromCacheEntryStatic(bytes);
     }
 
     /** static version of {@link #fromCacheEntry} for unit tests. */
     @VisibleForTesting
-    public static Package fromCacheEntryStatic(byte[] bytes) {
+    public static ParsedPackage fromCacheEntryStatic(byte[] bytes) {
         final Parcel p = Parcel.obtain();
         p.unmarshall(bytes, 0, bytes.length);
         p.setDataPosition(0);
@@ -1114,7 +1150,8 @@
         final ReadHelper helper = new ReadHelper(p);
         helper.startAndInstall();
 
-        PackageParser.Package pkg = new PackageParser.Package(p);
+        // TODO(b/135203078): Hide PackageImpl constructor?
+        ParsedPackage pkg = new PackageImpl(p);
 
         p.recycle();
 
@@ -1124,14 +1161,14 @@
     }
 
     @VisibleForTesting
-    protected byte[] toCacheEntry(Package pkg) {
+    protected byte[] toCacheEntry(ParsedPackage pkg) {
         return toCacheEntryStatic(pkg);
 
     }
 
     /** static version of {@link #toCacheEntry} for unit tests. */
     @VisibleForTesting
-    public static byte[] toCacheEntryStatic(Package pkg) {
+    public static byte[] toCacheEntryStatic(ParsedPackage pkg) {
         final Parcel p = Parcel.obtain();
         final WriteHelper helper = new WriteHelper(p);
 
@@ -1180,7 +1217,7 @@
      * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
      * or {@code null} if no cached result exists.
      */
-    private Package getCachedResult(File packageFile, int flags) {
+    public ParsedPackage getCachedResult(File packageFile, int flags) {
         if (mCacheDir == null) {
             return null;
         }
@@ -1195,9 +1232,9 @@
             }
 
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
-            Package p = fromCacheEntry(bytes);
+            ParsedPackage p = fromCacheEntry(bytes);
             if (mCallback != null) {
-                String[] overlayApks = mCallback.getOverlayApks(p.packageName);
+                String[] overlayApks = mCallback.getOverlayApks(p.getPackageName());
                 if (overlayApks != null && overlayApks.length > 0) {
                     for (String overlayApk : overlayApks) {
                         // If a static RRO is updated, return null.
@@ -1221,7 +1258,7 @@
     /**
      * Caches the parse result for {@code packageFile} with flags {@code flags}.
      */
-    private void cacheResult(File packageFile, int flags, Package parsed) {
+    public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
         if (mCacheDir == null) {
             return;
         }
@@ -1260,7 +1297,8 @@
      * split names.
      * <p>
      * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #collectCertificates(Package, int)}.
+     * must be done separately in
+     * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
      */
     private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
         final PackageLite lite = parseClusterPackageLite(packageDir, 0);
@@ -1324,10 +1362,11 @@
      * Parse the given APK file, treating it as as a single monolithic package.
      * <p>
      * Note that this <em>does not</em> perform signature verification; that
-     * must be done separately in {@link #collectCertificates(Package, int)}.
+     * must be done separately in
+     * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
      *
      * @deprecated external callers should move to
-     *             {@link #parsePackage(File, int)}. Eventually this method will
+     *             {@link #parseParsedPackage(File, int, boolean)}. Eventually this method will
      *             be marked private.
      */
     @Deprecated
@@ -1527,8 +1566,11 @@
      * Collect certificates from all the APKs described in the given package,
      * populating {@link Package#mSigningDetails}. Also asserts that all APK
      * contents are signed correctly and consistently.
+     *
+     * @deprecated use {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static void collectCertificates(Package pkg, boolean skipVerify)
             throws PackageParserException {
         collectCertificatesInternal(pkg, skipVerify);
@@ -1707,7 +1749,7 @@
                 ? null : "must have at least one '.' separator";
     }
 
-    private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
+    public static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
             AttributeSet attrs) throws IOException, XmlPullParserException,
             PackageParserException {
 
@@ -2487,8 +2529,6 @@
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return null;
 
-            } else if (tagName.equals("queries")) {
-                parseQueries(pkg, res, parser, flags, outError);
             } else {
                 Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                         + " at " + mArchiveSourcePath + " "
@@ -2958,7 +2998,7 @@
         return true;
     }
 
-    private static String buildClassName(String pkg, CharSequence clsSeq,
+    public static String buildClassName(String pkg, CharSequence clsSeq,
             String[] outError) {
         if (clsSeq == null || clsSeq.length() <= 0) {
             outError[0] = "Empty class name in package " + pkg;
@@ -3006,7 +3046,7 @@
         return proc;
     }
 
-    private static String buildProcessName(String pkg, String defProc,
+    public static String buildProcessName(String pkg, String defProc,
             CharSequence procSeq, int flags, String[] separateProcesses,
             String[] outError) {
         if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {
@@ -3026,7 +3066,7 @@
         return TextUtils.safeIntern(buildCompoundName(pkg, procSeq, "process", outError));
     }
 
-    private static String buildTaskAffinityName(String pkg, String defProc,
+    public static String buildTaskAffinityName(String pkg, String defProc,
             CharSequence procSeq, String[] outError) {
         if (procSeq == null) {
             return defProc;
@@ -3588,9 +3628,6 @@
             owner.mRequiredAccountType = requiredAccountType;
         }
 
-        owner.mForceQueryable =
-                sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false);
-
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
                 false)) {
@@ -4052,89 +4089,6 @@
         return true;
     }
 
-    private boolean parseQueries(Package owner, Resources res, XmlResourceParser parser, int flags,
-            String[] outError)
-            throws IOException, XmlPullParserException {
-
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG
-                || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            if (parser.getName().equals("intent")) {
-                QueriesIntentInfo intentInfo = new QueriesIntentInfo();
-                if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
-                        intentInfo, outError)) {
-                    return false;
-                }
-
-                Uri data = null;
-                String dataType = null;
-                String host = "";
-                final int numActions = intentInfo.countActions();
-                final int numSchemes = intentInfo.countDataSchemes();
-                final int numTypes = intentInfo.countDataTypes();
-                final int numHosts = intentInfo.getHosts().length;
-                if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
-                    outError[0] = "intent tags must contain either an action or data.";
-                    return false;
-                }
-                if (numActions > 1) {
-                    outError[0] = "intent tag may have at most one action.";
-                    return false;
-                }
-                if (numTypes > 1) {
-                    outError[0] = "intent tag may have at most one data type.";
-                    return false;
-                }
-                if (numSchemes > 1) {
-                    outError[0] = "intent tag may have at most one data scheme.";
-                    return false;
-                }
-                if (numHosts > 1) {
-                    outError[0] = "intent tag may have at most one data host.";
-                    return false;
-                }
-                Intent intent = new Intent();
-                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
-                    intent.addCategory(intentInfo.getCategory(i));
-                }
-                if (numHosts == 1) {
-                    host = intentInfo.getHosts()[0];
-                }
-                if (numSchemes == 1) {
-                    data = new Uri.Builder()
-                            .scheme(intentInfo.getDataScheme(0))
-                            .authority(host)
-                            .build();
-                }
-                if (numTypes == 1) {
-                    dataType = intentInfo.getDataType(0);
-                }
-                intent.setDataAndType(data, dataType);
-                if (numActions == 1) {
-                    intent.setAction(intentInfo.getAction(0));
-                }
-                owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent);
-            } else if (parser.getName().equals("package")) {
-                final TypedArray sa = res.obtainAttributes(parser,
-                        com.android.internal.R.styleable.AndroidManifestQueriesPackage);
-                final String packageName =
-                        sa.getString(R.styleable.AndroidManifestQueriesPackage_name);
-                if (TextUtils.isEmpty(packageName)) {
-                    outError[0] = "Package name is missing from package tag.";
-                    return false;
-                }
-                owner.mQueriesPackages =
-                        ArrayUtils.add(owner.mQueriesPackages, packageName.intern());
-            }
-        }
-        return true;
-    }
-
     /**
      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
      */
@@ -5846,7 +5800,7 @@
         return null;
     }
 
-    private static final String ANDROID_RESOURCES
+    public static final String ANDROID_RESOURCES
             = "http://schemas.android.com/apk/res/android";
 
     private boolean parseIntent(Resources res, XmlResourceParser parser, boolean allowGlobs,
@@ -6541,7 +6495,10 @@
     /**
      * Representation of a full package parsed from APK files on disk. A package
      * consists of a single base APK, and zero or more split APKs.
+     *
+     * @deprecated use an {@link AndroidPackage}
      */
+    @Deprecated
     public final static class Package implements Parcelable {
 
         @UnsupportedAppUsage
@@ -6649,9 +6606,6 @@
         // The major version code declared for this package.
         public int mVersionCodeMajor;
 
-        // Whether the package declares that it should be queryable by all normal apps on device.
-        public boolean mForceQueryable;
-
         // Return long containing mVersionCode and mVersionCodeMajor.
         public long getLongVersionCode() {
             return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
@@ -6757,9 +6711,6 @@
         /** Whether or not the package is a stub and must be replaced by the full version. */
         public boolean isStub;
 
-        public ArrayList<String> mQueriesPackages;
-        public ArrayList<Intent> mQueriesIntents;
-
         @UnsupportedAppUsage
         public Package(String packageName) {
             this.packageName = packageName;
@@ -7263,9 +7214,6 @@
             use32bitAbi = (dest.readInt() == 1);
             restrictUpdateHash = dest.createByteArray();
             visibleToInstantApps = dest.readInt() == 1;
-            mForceQueryable = dest.readBoolean();
-            mQueriesIntents = dest.createTypedArrayList(Intent.CREATOR);
-            mQueriesPackages = dest.createStringArrayList();
         }
 
         private static void internStringArrayList(List<String> list) {
@@ -7281,7 +7229,7 @@
          * Sets the package owner and the the {@code applicationInfo} for every component
          * owner by this package.
          */
-        private void fixupOwner(List<? extends Component<?>> list) {
+        public void fixupOwner(List<? extends Component<?>> list) {
             if (list != null) {
                 for (Component<?> c : list) {
                     c.owner = this;
@@ -7391,12 +7339,8 @@
             dest.writeInt(use32bitAbi ? 1 : 0);
             dest.writeByteArray(restrictUpdateHash);
             dest.writeInt(visibleToInstantApps ? 1 : 0);
-            dest.writeBoolean(mForceQueryable);
-            dest.writeTypedList(mQueriesIntents);
-            dest.writeList(mQueriesPackages);
         }
 
-
         /**
          * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
          */
@@ -7468,6 +7412,10 @@
         };
     }
 
+    /**
+     * @deprecated use a {@link ComponentParseUtils.ParsedComponent}
+     */
+    @Deprecated
     public static abstract class Component<II extends IntentInfo> {
         @UnsupportedAppUsage
         public final ArrayList<II> intents;
@@ -7648,6 +7596,10 @@
         }
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedPermission}
+     */
+    @Deprecated
     public final static class Permission extends Component<IntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final PermissionInfo info;
@@ -7722,6 +7674,10 @@
         };
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedPermissionGroup}
+     */
+    @Deprecated
     public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final PermissionGroupInfo info;
@@ -7821,7 +7777,12 @@
         return false;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
+     *      AndroidPackage, int, PackageUserState, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state) {
         return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
@@ -7878,7 +7839,12 @@
         ai.icon = (sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes : ai.iconRes;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
+     *      AndroidPackage, int, PackageUserState, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static ApplicationInfo generateApplicationInfo(Package p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
@@ -7918,6 +7884,11 @@
         return ai;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateApplicationInfo(
+     *      AndroidPackage, int, PackageUserState, int)}
+     */
+    @Deprecated
     public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags,
             PackageUserState state, int userId) {
         if (ai == null) return null;
@@ -7937,7 +7908,12 @@
         return ai;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generatePermissionInfo(
+     *      ComponentParseUtils.ParsedPermission, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final PermissionInfo generatePermissionInfo(
             Permission p, int flags) {
         if (p == null) return null;
@@ -7949,7 +7925,12 @@
         return pi;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generatePermissionGroupInfo(
+     *      ComponentParseUtils.ParsedPermissionGroup, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final PermissionGroupInfo generatePermissionGroupInfo(
             PermissionGroup pg, int flags) {
         if (pg == null) return null;
@@ -7961,6 +7942,10 @@
         return pgi;
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedActivity}
+     */
+    @Deprecated
     public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ActivityInfo info;
@@ -8076,7 +8061,12 @@
         };
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateActivityInfo(
+     *      AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final ActivityInfo generateActivityInfo(Activity a, int flags,
             PackageUserState state, int userId) {
         if (a == null) return null;
@@ -8094,6 +8084,11 @@
         return ai;
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateActivityInfo(
+     *      AndroidPackage, ComponentParseUtils.ParsedActivity, int, PackageUserState, int)}
+     */
+    @Deprecated
     public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags,
             PackageUserState state, int userId) {
         if (ai == null) return null;
@@ -8107,6 +8102,10 @@
         return ai;
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedService}
+     */
+    @Deprecated
     public final static class Service extends Component<ServiceIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ServiceInfo info;
@@ -8168,7 +8167,12 @@
         };
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateServiceInfo(
+     * AndroidPackage, ComponentParseUtils.ParsedService, int, PackageUserState, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final ServiceInfo generateServiceInfo(Service s, int flags,
             PackageUserState state, int userId) {
         if (s == null) return null;
@@ -8186,6 +8190,10 @@
         return si;
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedProvider}
+     */
+    @Deprecated
     public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable {
         @UnsupportedAppUsage
         public final ProviderInfo info;
@@ -8266,7 +8274,12 @@
         };
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateProviderInfo(
+     *      AndroidPackage, ComponentParseUtils.ParsedProvider, int, PackageUserState, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final ProviderInfo generateProviderInfo(Provider p, int flags,
             PackageUserState state, int userId) {
         if (p == null) return null;
@@ -8289,6 +8302,10 @@
         return pi;
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedInstrumentation}
+     */
+    @Deprecated
     public final static class Instrumentation extends Component<IntentInfo> implements
             Parcelable {
         @UnsupportedAppUsage
@@ -8349,7 +8366,12 @@
         };
     }
 
+    /**
+     * @deprecated use {@link PackageInfoUtils#generateInstrumentationInfo(
+     *      ComponentParseUtils.ParsedInstrumentation, int)}
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public static final InstrumentationInfo generateInstrumentationInfo(
             Instrumentation i, int flags) {
         if (i == null) return null;
@@ -8361,6 +8383,10 @@
         return ii;
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedIntentInfo}
+     */
+    @Deprecated
     public static abstract class IntentInfo extends IntentFilter {
         @UnsupportedAppUsage
         public boolean hasDefault;
@@ -8404,8 +8430,10 @@
         }
     }
 
-    public static final class QueriesIntentInfo extends IntentInfo {}
-
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedActivityIntentInfo}
+     */
+    @Deprecated
     public final static class ActivityIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Activity activity;
@@ -8429,6 +8457,10 @@
         }
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedServiceIntentInfo}
+     */
+    @Deprecated
     public final static class ServiceIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Service service;
@@ -8452,6 +8484,10 @@
         }
     }
 
+    /**
+     * @deprecated use {@link ComponentParseUtils.ParsedProviderIntentInfo}
+     */
+    @Deprecated
     public static final class ProviderIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Provider provider;
diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java
deleted file mode 100644
index 1565d9c..0000000
--- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-
-/**
- * Base for classes that update a {@link PackageParser.Package}'s shared libraries.
- *
- * @hide
- */
-@VisibleForTesting
-public abstract class PackageSharedLibraryUpdater {
-
-    /**
-     * Update the package's shared libraries.
-     *
-     * @param pkg the package to update.
-     */
-    public abstract void updatePackage(PackageParser.Package pkg);
-
-    static void removeLibrary(PackageParser.Package pkg, String libraryName) {
-        pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName);
-        pkg.usesOptionalLibraries =
-                ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName);
-    }
-
-    static @NonNull
-            <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
-        if (cur == null) {
-            cur = new ArrayList<>();
-        }
-        cur.add(0, val);
-        return cur;
-    }
-
-    private static boolean isLibraryPresent(ArrayList<String> usesLibraries,
-            ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) {
-        return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
-                || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
-    }
-
-    /**
-     * Add an implicit dependency.
-     *
-     * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with
-     * the {@code implicitDependency} if it is not already in the list of libraries.
-     *
-     * @param pkg the {@link PackageParser.Package} to update.
-     * @param existingLibrary the existing library.
-     * @param implicitDependency the implicit dependency to add
-     */
-    void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary,
-            String implicitDependency) {
-        ArrayList<String> usesLibraries = pkg.usesLibraries;
-        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
-        if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
-            if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
-                prefix(usesLibraries, implicitDependency);
-            } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
-                prefix(usesOptionalLibraries, implicitDependency);
-            }
-
-            pkg.usesLibraries = usesLibraries;
-            pkg.usesOptionalLibraries = usesOptionalLibraries;
-        }
-    }
-
-    void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) {
-        ArrayList<String> usesLibraries = pkg.usesLibraries;
-        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
-        boolean alreadyPresent = isLibraryPresent(
-                usesLibraries, usesOptionalLibraries, libraryName);
-        if (!alreadyPresent) {
-            usesLibraries = prefix(usesLibraries, libraryName);
-
-            pkg.usesLibraries = usesLibraries;
-        }
-    }
-}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 249b691..b271ccd 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -28,6 +28,7 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.annotation.UnsupportedAppUsage;
+import android.content.pm.parsing.ComponentParseUtils;
 import android.os.BaseBundle;
 import android.os.Debug;
 import android.os.PersistableBundle;
@@ -127,6 +128,18 @@
                         && (!this.hidden || matchUninstalled));
     }
 
+    public boolean isMatch(ComponentInfo componentInfo, int flags) {
+        return isMatch(componentInfo.applicationInfo.isSystemApp(),
+                componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.directBootAware, componentInfo.name, flags);
+    }
+
+    public boolean isMatch(boolean isSystem, boolean isPackageEnabled,
+            ComponentParseUtils.ParsedComponent component, int flags) {
+        return isMatch(isSystem, isPackageEnabled, component.isEnabled(),
+                component.isDirectBootAware(), component.getName(), flags);
+    }
+
     /**
      * Test if the given component is considered installed, enabled and a match
      * for the given flags.
@@ -135,28 +148,33 @@
      * Expects at least one of {@link PackageManager#MATCH_DIRECT_BOOT_AWARE} and
      * {@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} are specified in {@code flags}.
      * </p>
+     *
      */
-    public boolean isMatch(ComponentInfo componentInfo, int flags) {
-        final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
+    public boolean isMatch(boolean isSystem, boolean isPackageEnabled, boolean isComponentEnabled,
+               boolean isComponentDirectBootAware, String componentName, int flags) {
         final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
-        if (!isAvailable(flags)
-                && !(isSystemApp && matchUninstalled)) return reportIfDebug(false, flags);
-        if (!isEnabled(componentInfo, flags)) return reportIfDebug(false, flags);
+        if (!isAvailable(flags) && !(isSystem && matchUninstalled)) {
+            return reportIfDebug(false, flags);
+        }
+
+        if (!isEnabled(isPackageEnabled, isComponentEnabled, componentName, flags)) {
+            return reportIfDebug(false, flags);
+        }
 
         if ((flags & MATCH_SYSTEM_ONLY) != 0) {
-            if (!isSystemApp) {
+            if (!isSystem) {
                 return reportIfDebug(false, flags);
             }
         }
 
         final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
-                && !componentInfo.directBootAware;
+                && !isComponentDirectBootAware;
         final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
-                && componentInfo.directBootAware;
+                && isComponentDirectBootAware;
         return reportIfDebug(matchesUnaware || matchesAware, flags);
     }
 
-    private boolean reportIfDebug(boolean result, int flags) {
+    public boolean reportIfDebug(boolean result, int flags) {
         if (DEBUG && !result) {
             Slog.i(LOG_TAG, "No match!; flags: "
                     + DebugUtils.flagsToString(PackageManager.class, "MATCH_", flags) + " "
@@ -165,10 +183,22 @@
         return result;
     }
 
+    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+        return isEnabled(componentInfo.applicationInfo.enabled, componentInfo.enabled,
+                componentInfo.name, flags);
+    }
+
+    public boolean isEnabled(boolean isPackageEnabled,
+            ComponentParseUtils.ParsedComponent parsedComponent, int flags) {
+        return isEnabled(isPackageEnabled, parsedComponent.isEnabled(), parsedComponent.getName(),
+                flags);
+    }
+
     /**
      * Test if the given component is considered enabled.
      */
-    public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+    public boolean isEnabled(boolean isPackageEnabled, boolean isComponentEnabled,
+            String componentName, int flags) {
         if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
             return true;
         }
@@ -183,24 +213,26 @@
                 if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
                     return false;
                 }
+                // fallthrough
             case COMPONENT_ENABLED_STATE_DEFAULT:
-                if (!componentInfo.applicationInfo.enabled) {
+                if (!isPackageEnabled) {
                     return false;
                 }
+                // fallthrough
             case COMPONENT_ENABLED_STATE_ENABLED:
                 break;
         }
 
         // Check if component has explicit state before falling through to
         // the manifest default
-        if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
+        if (ArrayUtils.contains(this.enabledComponents, componentName)) {
             return true;
         }
-        if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
+        if (ArrayUtils.contains(this.disabledComponents, componentName)) {
             return false;
         }
 
-        return componentInfo.enabled;
+        return isComponentEnabled;
     }
 
     @Override
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 3488cc3..2863b26 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,20 +39,24 @@
 public final class SharedLibraryInfo implements Parcelable {
 
     /** @hide */
-    public static SharedLibraryInfo createForStatic(PackageParser.Package pkg) {
-        return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(),
-                pkg.staticSharedLibName,
-                pkg.staticSharedLibVersion,
+    public static SharedLibraryInfo createForStatic(AndroidPackage pkg) {
+        return new SharedLibraryInfo(null, pkg.getPackageName(),
+                pkg.makeListAllCodePaths(),
+                pkg.getStaticSharedLibName(),
+                pkg.getStaticSharedLibVersion(),
                 TYPE_STATIC,
-                new VersionedPackage(pkg.manifestPackageName, pkg.getLongVersionCode()),
+                new VersionedPackage(pkg.getManifestPackageName(),
+                        pkg.getLongVersionCode()),
                 null, null);
     }
 
     /** @hide */
-    public static SharedLibraryInfo createForDynamic(PackageParser.Package pkg, String name) {
-        return new SharedLibraryInfo(null, pkg.packageName, pkg.getAllCodePaths(), name,
+    public static SharedLibraryInfo createForDynamic(AndroidPackage pkg, String name) {
+        return new SharedLibraryInfo(null, pkg.getPackageName(),
+                pkg.makeListAllCodePaths(), name,
                 (long) VERSION_UNDEFINED,
-                TYPE_DYNAMIC, new VersionedPackage(pkg.packageName, pkg.getLongVersionCode()),
+                TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(),
+                pkg.getLongVersionCode()),
                 null, null);
     }
 
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 5d10b88..4cd201f 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.AndroidPackage;
 import android.util.ArrayMap;
 import android.util.jar.StrictJarFile;
 
@@ -86,8 +87,8 @@
      *
      * NOTE: involves I/O checks.
      */
-    public static Map<String, String> getPackageDexMetadata(PackageParser.Package pkg) {
-        return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+    public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) {
+        return buildPackageApkToDexMetadataMap(pkg.makeListAllCodePaths());
     }
 
     /**
@@ -160,7 +161,7 @@
      *
      * @throws PackageParserException in case of errors.
      */
-    public static void validatePackageDexMetadata(PackageParser.Package pkg)
+    public static void validatePackageDexMetadata(AndroidPackage pkg)
             throws PackageParserException {
         Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
         for (String dexMetadata : apkToDexMetadataList) {
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.aidl b/core/java/android/content/pm/parsing/AndroidPackage.aidl
new file mode 100644
index 0000000..ab3cf7c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/AndroidPackage.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm.parsing;
+
+/* @hide */
+parcelable AndroidPackage;
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
new file mode 100644
index 0000000..bef984d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.pm.parsing;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * The last state of a package during parsing/install before it is available in
+ * {@link com.android.server.pm.PackageManagerService#mPackages}.
+ *
+ * It is the responsibility of the caller to understand what data is available at what step of the
+ * parsing or install process.
+ *
+ * TODO(b/135203078): Nullability annotations
+ * TODO(b/135203078): Remove get/setAppInfo differences
+ *
+ * @hide
+ */
+public interface AndroidPackage extends Parcelable {
+
+    /**
+     * This will eventually be removed. Avoid calling this at all costs.
+     */
+    @Deprecated
+    AndroidPackageWrite mutate();
+
+    boolean canHaveOatDir();
+
+    boolean cantSaveState();
+
+    List<String> getAdoptPermissions();
+
+    List<String> getAllCodePaths();
+
+    List<String> getAllCodePathsExcludingResourceOnly();
+
+    String getAppComponentFactory();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getClassLoaderName()}
+     */
+    @Deprecated
+    String getAppInfoClassLoaderName();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getCodePath()}
+     */
+    @Deprecated
+    String getAppInfoCodePath();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getName()}
+     */
+    @Deprecated
+    String getAppInfoName();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getPackageName()}
+     */
+    @Deprecated
+    String getAppInfoPackageName();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getProcessName()}
+     */
+    @Deprecated
+    String getAppInfoProcessName();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getCodePath()}
+     */
+    @Deprecated
+    String getAppInfoResourcePath();
+
+    Bundle getAppMetaData();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #getVolumeUuid()}
+     */
+    @Deprecated
+    String getApplicationInfoVolumeUuid();
+
+    String getBackupAgentName();
+
+    int getBanner();
+
+    String getBaseCodePath();
+
+    int getBaseRevisionCode();
+
+    int getCategory();
+
+    String getClassLoaderName();
+
+    String getClassName();
+
+    String getCodePath();
+
+    int getCompatibleWidthLimitDp();
+
+    int getCompileSdkVersion();
+
+    String getCompileSdkVersionCodeName();
+
+    @Nullable
+    List<ConfigurationInfo> getConfigPreferences();
+
+    String getCpuAbiOverride();
+
+    String getCredentialProtectedDataDir();
+
+    String getDataDir();
+
+    int getDescriptionRes();
+
+    String getDeviceProtectedDataDir();
+
+    List<FeatureGroupInfo> getFeatureGroups();
+
+    int getFlags();
+
+    int getFullBackupContent();
+
+    int getHiddenApiEnforcementPolicy();
+
+    int getIcon();
+
+    int getIconRes();
+
+    List<String> getImplicitPermissions();
+
+    int getInstallLocation();
+
+    Map<String, ArraySet<PublicKey>> getKeySetMapping();
+
+    int getLabelRes();
+
+    int getLargestWidthLimitDp();
+
+    long[] getLastPackageUsageTimeInMills();
+
+    long getLatestForegroundPackageUseTimeInMills();
+
+    long getLatestPackageUseTimeInMills();
+
+    List<String> getLibraryNames();
+
+    int getLogo();
+
+    long getLongVersionCode();
+
+    String getManageSpaceActivityName();
+
+    String getManifestPackageName();
+
+    float getMaxAspectRatio();
+
+    Bundle getMetaData(); // TODO(b/135203078): Make all the Bundles immutable
+
+    float getMinAspectRatio();
+
+    int getMinSdkVersion();
+
+    String getName();
+
+    String getNativeLibraryDir();
+
+    String getNativeLibraryRootDir();
+
+    int getNetworkSecurityConfigRes();
+
+    CharSequence getNonLocalizedLabel();
+
+    @Nullable
+    List<String> getOriginalPackages();
+
+    String getOverlayCategory();
+
+    int getOverlayPriority();
+
+    String getOverlayTarget();
+
+    String getOverlayTargetName();
+
+    // TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
+    //  The refactor makes them the same value with no known consequences, so should be redundant.
+    String getPackageName();
+
+    @Nullable
+    List<ParsedActivity> getActivities();
+
+    @Nullable
+    List<ParsedInstrumentation> getInstrumentations();
+
+    @Nullable
+    List<ParsedPermissionGroup> getPermissionGroups();
+
+    @Nullable
+    List<ParsedPermission> getPermissions();
+
+    @Nullable
+    List<ParsedProvider> getProviders();
+
+    @Nullable
+    List<ParsedActivity> getReceivers();
+
+    @Nullable
+    List<ParsedService> getServices();
+
+    String getPermission();
+
+    @Nullable
+    List<ParsedActivityIntentInfo> getPreferredActivityFilters();
+
+    int getPreferredOrder();
+
+    String getPrimaryCpuAbi();
+
+    int getPrivateFlags();
+
+    String getProcessName();
+
+    @Nullable
+    List<String> getProtectedBroadcasts();
+
+    String getPublicSourceDir();
+
+    List<Intent> getQueriesIntents();
+
+    List<String> getQueriesPackages();
+
+    String getRealPackage();
+
+    // TODO(b/135203078): Rename to getRequiredFeatures? Somewhat ambigious whether "Req" is
+    //  required or requested.
+    @Nullable
+    List<FeatureInfo> getReqFeatures();
+
+    List<String> getRequestedPermissions();
+
+    String getRequiredAccountType();
+
+    int getRequiresSmallestWidthDp();
+
+    byte[] getRestrictUpdateHash();
+
+    String getRestrictedAccountType();
+
+    int getRoundIconRes();
+
+    String getScanPublicSourceDir();
+
+    String getScanSourceDir();
+
+    String getSeInfo();
+
+    String getSeInfoUser();
+
+    String getSecondaryCpuAbi();
+
+    String getSecondaryNativeLibraryDir();
+
+    String getSharedUserId();
+
+    int getSharedUserLabel();
+
+    PackageParser.SigningDetails getSigningDetails();
+
+    String[] getSplitClassLoaderNames();
+
+    @Nullable
+    String[] getSplitCodePaths();
+
+    @Nullable
+    SparseArray<int[]> getSplitDependencies();
+
+    int[] getSplitFlags();
+
+    String[] getSplitNames();
+
+    String[] getSplitPublicSourceDirs();
+
+    int[] getSplitRevisionCodes();
+
+    String getStaticSharedLibName();
+
+    long getStaticSharedLibVersion();
+
+    // TODO(b/135203078): Return String directly
+    UUID getStorageUuid();
+
+    int getTargetSandboxVersion();
+
+    int getTargetSdkVersion();
+
+    String getTaskAffinity();
+
+    int getTheme();
+
+    int getUiOptions();
+
+    int getUid();
+
+    Set<String> getUpgradeKeySets();
+
+    @Nullable
+    List<String> getUsesLibraries();
+
+    @Nullable
+    String[] getUsesLibraryFiles();
+
+    List<SharedLibraryInfo> getUsesLibraryInfos();
+
+    @Nullable
+    List<String> getUsesOptionalLibraries();
+
+    @Nullable
+    List<String> getUsesStaticLibraries();
+
+    @Nullable
+    String[][] getUsesStaticLibrariesCertDigests();
+
+    @Nullable
+    long[] getUsesStaticLibrariesVersions();
+
+    int getVersionCode();
+
+    int getVersionCodeMajor();
+
+    String getVersionName();
+
+    String getVolumeUuid();
+
+    String getZygotePreloadName();
+
+    boolean hasComponentClassName(String className);
+
+    // App Info
+
+    boolean hasRequestedLegacyExternalStorage();
+
+    boolean isBaseHardwareAccelerated();
+
+    boolean isCoreApp();
+
+    boolean isDefaultToDeviceProtectedStorage();
+
+    boolean isDirectBootAware();
+
+    boolean isEmbeddedDexUsed();
+
+    boolean isEnabled();
+
+    boolean isEncryptionAware();
+
+    boolean isExternal();
+
+    boolean isForceQueryable();
+
+    boolean isForwardLocked();
+
+    boolean isHiddenUntilInstalled();
+
+    boolean isInstantApp();
+
+    boolean isInternal();
+
+    boolean isLibrary();
+
+    // TODO(b/135203078): Should probably be in a utility class
+    boolean isMatch(int flags);
+
+    boolean isNativeLibraryRootRequiresIsa();
+
+    boolean isOem();
+
+    boolean isOverlayIsStatic();
+
+    boolean isPrivileged();
+
+    boolean isProduct();
+
+    boolean isProfileableByShell();
+
+    boolean isRequiredForAllUsers();
+
+    boolean isStaticSharedLibrary();
+
+    boolean isStub();
+
+    boolean isSystem(); // TODO(b/135203078): Collapse with isSystemApp, should be exactly the same.
+
+    boolean isSystemApp();
+
+    boolean isSystemExt();
+
+    boolean isUpdatedSystemApp();
+
+    boolean isUse32BitAbi();
+
+    boolean isVendor();
+
+    boolean isVisibleToInstantApps();
+
+    List<String> makeListAllCodePaths(); // TODO(b/135203078): Collapse with getAllCodePaths
+
+    boolean requestsIsolatedSplitLoading();
+
+    ApplicationInfo toAppInfo();
+
+    Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
+        @Override
+        public PackageImpl createFromParcel(Parcel source) {
+            return new PackageImpl(source);
+        }
+
+        @Override
+        public PackageImpl[] newArray(int size) {
+            return new PackageImpl[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/parsing/AndroidPackageWrite.java b/core/java/android/content/pm/parsing/AndroidPackageWrite.java
new file mode 100644
index 0000000..b7595d2
--- /dev/null
+++ b/core/java/android/content/pm/parsing/AndroidPackageWrite.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+
+import java.util.List;
+
+/**
+ * Contains remaining mutable fields after package parsing has completed.
+ *
+ * Most are state that can probably be tracked outside of the AndroidPackage object. New methods
+ * should never be added to this interface.
+ *
+ * TODO(b/135203078): Remove entirely
+ *
+ * @deprecated the eventual goal is that the object returned from parsing represents exactly what
+ * was parsed from the APK, and so further mutation should be disallowed,
+ * with any state being stored in another class
+ *
+ * @hide
+ */
+@Deprecated
+public interface AndroidPackageWrite extends AndroidPackage {
+
+    AndroidPackageWrite setUsesLibraryFiles(@Nullable String[] usesLibraryFiles);
+
+    // TODO(b/135203078): Remove or use a non-system wide representation of the shared libraries;
+    //  this doesn't represent what was parsed from the APK
+    AndroidPackageWrite setUsesLibraryInfos(@Nullable List<SharedLibraryInfo> usesLibraryInfos);
+
+    AndroidPackageWrite setHiddenUntilInstalled(boolean hidden);
+
+    AndroidPackageWrite setUpdatedSystemApp(boolean updatedSystemApp);
+
+    AndroidPackageWrite setLastPackageUsageTimeInMills(int reason, long time);
+
+    AndroidPackageWrite setPrimaryCpuAbi(String primaryCpuAbi);
+
+    AndroidPackageWrite setSeInfo(String seInfo);
+
+    AndroidPackageWrite setSigningDetails(PackageParser.SigningDetails signingDetails);
+}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
new file mode 100644
index 0000000..ac2e373
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.annotation.UnsupportedAppUsage;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.VerifierInfo;
+import android.content.res.ApkAssets;
+import android.content.res.XmlResourceParser;
+import android.os.Trace;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** @hide */
+public class ApkLiteParseUtils {
+
+    private static final String TAG = ApkParseUtils.TAG;
+
+    // TODO(b/135203078): Consolidate constants
+    private static final int DEFAULT_MIN_SDK_VERSION = 1;
+    private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
+    private static final int PARSE_DEFAULT_INSTALL_LOCATION =
+            PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+
+    /**
+     * Parse only lightweight details about the package at the given location.
+     * Automatically detects if the package is a monolithic style (single APK
+     * file) or cluster style (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     *
+     * @see PackageParser#parsePackage(File, int)
+     */
+    @UnsupportedAppUsage
+    public static PackageParser.PackageLite parsePackageLite(File packageFile, int flags)
+            throws PackageParser.PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackageLite(packageFile, flags);
+        } else {
+            return parseMonolithicPackageLite(packageFile, flags);
+        }
+    }
+
+    public static PackageParser.PackageLite parseMonolithicPackageLite(File packageFile, int flags)
+            throws PackageParser.PackageParserException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final PackageParser.ApkLite baseApk = parseApkLite(packageFile, flags);
+        final String packagePath = packageFile.getAbsolutePath();
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        return new PackageParser.PackageLite(packagePath, baseApk, null, null, null, null,
+                null, null);
+    }
+
+    public static PackageParser.PackageLite parseClusterPackageLite(File packageDir, int flags)
+            throws PackageParser.PackageParserException {
+        final File[] files = packageDir.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split");
+        }
+
+        String packageName = null;
+        int versionCode = 0;
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
+        final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>();
+        for (File file : files) {
+            if (PackageParser.isApkFile(file)) {
+                final PackageParser.ApkLite lite = parseApkLite(file, flags);
+
+                // Assert that all package names and version codes are
+                // consistent with the first one we encounter.
+                if (packageName == null) {
+                    packageName = lite.packageName;
+                    versionCode = lite.versionCode;
+                } else {
+                    if (!packageName.equals(lite.packageName)) {
+                        throw new PackageParser.PackageParserException(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent package " + lite.packageName + " in " + file
+                                        + "; expected " + packageName);
+                    }
+                    if (versionCode != lite.versionCode) {
+                        throw new PackageParser.PackageParserException(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent version " + lite.versionCode + " in " + file
+                                        + "; expected " + versionCode);
+                    }
+                }
+
+                // Assert that each split is defined only oncuses-static-libe
+                if (apks.put(lite.splitName, lite) != null) {
+                    throw new PackageParser.PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split name " + lite.splitName
+                                    + " defined more than once; most recent was " + file);
+                }
+            }
+        }
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+        final PackageParser.ApkLite baseApk = apks.remove(null);
+        if (baseApk == null) {
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Missing base APK in " + packageDir);
+        }
+
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+
+        String[] splitNames = null;
+        boolean[] isFeatureSplits = null;
+        String[] usesSplitNames = null;
+        String[] configForSplits = null;
+        String[] splitCodePaths = null;
+        int[] splitRevisionCodes = null;
+        if (size > 0) {
+            splitNames = new String[size];
+            isFeatureSplits = new boolean[size];
+            usesSplitNames = new String[size];
+            configForSplits = new String[size];
+            splitCodePaths = new String[size];
+            splitRevisionCodes = new int[size];
+
+            splitNames = apks.keySet().toArray(splitNames);
+            Arrays.sort(splitNames, PackageParser.sSplitNameComparator);
+
+            for (int i = 0; i < size; i++) {
+                final PackageParser.ApkLite apk = apks.get(splitNames[i]);
+                usesSplitNames[i] = apk.usesSplitName;
+                isFeatureSplits[i] = apk.isFeatureSplit;
+                configForSplits[i] = apk.configForSplit;
+                splitCodePaths[i] = apk.codePath;
+                splitRevisionCodes[i] = apk.revisionCode;
+            }
+        }
+
+        final String codePath = packageDir.getAbsolutePath();
+        return new PackageParser.PackageLite(codePath, baseApk, splitNames, isFeatureSplits,
+                usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes);
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param apkFile path to a single APK
+     * @param flags optional parse flags, such as
+     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     */
+    public static PackageParser.ApkLite parseApkLite(File apkFile, int flags)
+            throws PackageParser.PackageParserException {
+        return parseApkLiteInner(apkFile, null, null, flags);
+    }
+
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param fd already open file descriptor of an apk file
+     * @param debugPathName arbitrary text name for this file, for debug output
+     * @param flags optional parse flags, such as
+     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     */
+    public static PackageParser.ApkLite parseApkLite(FileDescriptor fd, String debugPathName,
+            int flags) throws PackageParser.PackageParserException {
+        return parseApkLiteInner(null, fd, debugPathName, flags);
+    }
+
+    private static PackageParser.ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd,
+            String debugPathName, int flags) throws PackageParser.PackageParserException {
+        final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
+
+        XmlResourceParser parser = null;
+        ApkAssets apkAssets = null;
+        try {
+            try {
+                apkAssets = fd != null
+                        ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+                        : ApkAssets.loadFromPath(apkPath);
+            } catch (IOException e) {
+                throw new PackageParser.PackageParserException(
+                        PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse " + apkPath, e);
+            }
+
+            parser = apkAssets.openXml(PackageParser.ANDROID_MANIFEST_FILENAME);
+
+            final PackageParser.SigningDetails signingDetails;
+            if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+                final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+                try {
+                    signingDetails =
+                            ApkParseUtils.collectCertificates(apkFile.getAbsolutePath(), skipVerify,
+                                    false, PackageParser.SigningDetails.UNKNOWN);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+            } else {
+                signingDetails = PackageParser.SigningDetails.UNKNOWN;
+            }
+
+            final AttributeSet attrs = parser;
+            return parseApkLite(apkPath, parser, attrs, signingDetails);
+
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            Slog.w(TAG, "Failed to parse " + apkPath, e);
+            throw new PackageParser.PackageParserException(
+                    PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to parse " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+            if (apkAssets != null) {
+                try {
+                    apkAssets.close();
+                } catch (Throwable ignored) {
+                }
+            }
+            // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
+        }
+    }
+
+    private static PackageParser.ApkLite parseApkLite(
+            String codePath, XmlPullParser parser, AttributeSet attrs,
+            PackageParser.SigningDetails signingDetails)
+            throws IOException, XmlPullParserException, PackageParser.PackageParserException {
+        final Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(
+                parser, attrs);
+
+        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+        int versionCode = 0;
+        int versionCodeMajor = 0;
+        int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
+        int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
+        int revisionCode = 0;
+        boolean coreApp = false;
+        boolean debuggable = false;
+        boolean multiArch = false;
+        boolean use32bitAbi = false;
+        boolean extractNativeLibs = true;
+        boolean isolatedSplits = false;
+        boolean isFeatureSplit = false;
+        boolean isSplitRequired = false;
+        boolean useEmbeddedDex = false;
+        String configForSplit = null;
+        String usesSplitName = null;
+
+        for (int i = 0; i < attrs.getAttributeCount(); i++) {
+            final String attr = attrs.getAttributeName(i);
+            switch (attr) {
+                case "installLocation":
+                    installLocation = attrs.getAttributeIntValue(i,
+                            PARSE_DEFAULT_INSTALL_LOCATION);
+                    break;
+                case "versionCode":
+                    versionCode = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "versionCodeMajor":
+                    versionCodeMajor = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "revisionCode":
+                    revisionCode = attrs.getAttributeIntValue(i, 0);
+                    break;
+                case "coreApp":
+                    coreApp = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "isolatedSplits":
+                    isolatedSplits = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "configForSplit":
+                    configForSplit = attrs.getAttributeValue(i);
+                    break;
+                case "isFeatureSplit":
+                    isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
+                    break;
+                case "isSplitRequired":
+                    isSplitRequired = attrs.getAttributeBooleanValue(i, false);
+                    break;
+            }
+        }
+
+        // Only search the tree when the tag is the direct child of <manifest> tag
+        int type;
+        final int searchDepth = parser.getDepth() + 1;
+
+        final List<VerifierInfo> verifiers = new ArrayList<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getDepth() != searchDepth) {
+                continue;
+            }
+
+            if (PackageParser.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+                final VerifierInfo verifier = parseVerifier(attrs);
+                if (verifier != null) {
+                    verifiers.add(verifier);
+                }
+            } else if (PackageParser.TAG_APPLICATION.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    switch (attr) {
+                        case "debuggable":
+                            debuggable = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "multiArch":
+                            multiArch = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "use32bitAbi":
+                            use32bitAbi = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                        case "extractNativeLibs":
+                            extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
+                            break;
+                        case "useEmbeddedDex":
+                            useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
+                            break;
+                    }
+                }
+            } else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) {
+                if (usesSplitName != null) {
+                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
+                    continue;
+                }
+
+                usesSplitName = attrs.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name");
+                if (usesSplitName == null) {
+                    throw new PackageParser.PackageParserException(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "<uses-split> tag requires 'android:name' attribute");
+                }
+            } else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("targetSdkVersion".equals(attr)) {
+                        targetSdkVersion = attrs.getAttributeIntValue(i,
+                                DEFAULT_TARGET_SDK_VERSION);
+                    }
+                    if ("minSdkVersion".equals(attr)) {
+                        minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
+                    }
+                }
+            }
+        }
+
+        return new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
+                isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+                versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
+                coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
+                isolatedSplits, minSdkVersion, targetSdkVersion);
+    }
+
+    public static VerifierInfo parseVerifier(AttributeSet attrs) {
+        String packageName = null;
+        String encodedPublicKey = null;
+
+        final int attrCount = attrs.getAttributeCount();
+        for (int i = 0; i < attrCount; i++) {
+            final int attrResId = attrs.getAttributeNameResource(i);
+            switch (attrResId) {
+                case R.attr.name:
+                    packageName = attrs.getAttributeValue(i);
+                    break;
+
+                case R.attr.publicKey:
+                    encodedPublicKey = attrs.getAttributeValue(i);
+                    break;
+            }
+        }
+
+        if (packageName == null || packageName.length() == 0) {
+            Slog.i(TAG, "verifier package name was null; skipping");
+            return null;
+        }
+
+        final PublicKey publicKey = PackageParser.parsePublicKey(encodedPublicKey);
+        if (publicKey == null) {
+            Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
+            return null;
+        }
+
+        return new VerifierInfo(packageName, publicKey);
+    }
+}
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
new file mode 100644
index 0000000..0f35b27
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -0,0 +1,3197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.content.pm.split.DefaultSplitAssetLoader;
+import android.content.pm.split.SplitAssetDependencyLoader;
+import android.content.pm.split.SplitAssetLoader;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.R;
+import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/** @hide */
+public class ApkParseUtils {
+
+    // TODO(b/135203078): Consolidate log tags
+    static final String TAG = "PackageParsing";
+
+    /**
+     * Parse the package at the given location. Automatically detects if the
+     * package is a monolithic style (single APK file) or cluster style
+     * (directory of APKs).
+     * <p>
+     * This performs sanity checking on cluster style packages, such as
+     * requiring identical package name and version codes, a single base APK,
+     * and unique split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}.
+     *
+     * If {@code useCaches} is true, the package parser might return a cached
+     * result from a previous parse of the same {@code packageFile} with the same
+     * {@code flags}. Note that this method does not check whether {@code packageFile}
+     * has changed since the last parse, it's up to callers to do so.
+     *
+     * @see PackageParser#parsePackageLite(File, int)
+     */
+    public static ParsingPackage parsePackage(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            DisplayMetrics displayMetrics,
+            boolean onlyCoreApps,
+            File packageFile,
+            int flags
+    ) throws PackageParserException {
+        if (packageFile.isDirectory()) {
+            return parseClusterPackage(parseInput, separateProcesses, callback, displayMetrics,
+                    onlyCoreApps, packageFile, flags);
+        } else {
+            return parseMonolithicPackage(parseInput, separateProcesses, callback, displayMetrics,
+                    onlyCoreApps, packageFile, flags);
+        }
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs sanity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}.
+     */
+    private static ParsingPackage parseClusterPackage(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            DisplayMetrics displayMetrics,
+            boolean onlyCoreApps,
+            File packageDir,
+            int flags
+    ) throws PackageParserException {
+        final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
+                0);
+        if (onlyCoreApps && !lite.coreApp) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Not a coreApp: " + packageDir);
+        }
+
+        // Build the split dependency tree.
+        SparseArray<int[]> splitDependencies = null;
+        final SplitAssetLoader assetLoader;
+        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+            try {
+                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+            }
+        } else {
+            assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        }
+
+        try {
+            final AssetManager assets = assetLoader.getBaseAssetManager();
+            final File baseApk = new File(lite.baseCodePath);
+            ParsingPackage parsingPackage = parseBaseApk(parseInput, separateProcesses, callback,
+                    displayMetrics, baseApk, assets, flags);
+            if (parsingPackage == null) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse base APK: " + baseApk);
+            }
+
+            if (!ArrayUtils.isEmpty(lite.splitNames)) {
+                parsingPackage.asSplit(
+                        lite.splitNames,
+                        lite.splitCodePaths,
+                        lite.splitRevisionCodes,
+                        splitDependencies
+                );
+                final int num = lite.splitNames.length;
+
+                for (int i = 0; i < num; i++) {
+                    final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+                    parseSplitApk(parseInput, displayMetrics, separateProcesses, parsingPackage, i,
+                            splitAssets, flags);
+                }
+            }
+
+            return parsingPackage.setCodePath(packageDir.getCanonicalPath())
+                    .setUse32BitAbi(lite.use32bitAbi);
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to get path: " + lite.baseCodePath, e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(AndroidPackage, boolean)}.
+     */
+    public static ParsingPackage parseMonolithicPackage(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            DisplayMetrics displayMetrics,
+            boolean onlyCoreApps,
+            File apkFile,
+            int flags
+    ) throws PackageParserException {
+        final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
+                flags);
+        if (onlyCoreApps) {
+            if (!lite.coreApp) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Not a coreApp: " + apkFile);
+            }
+        }
+
+        final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
+        try {
+            return parseBaseApk(parseInput, separateProcesses, callback,
+                    displayMetrics, apkFile, assetLoader.getBaseAssetManager(), flags)
+                    .setCodePath(apkFile.getCanonicalPath())
+                    .setUse32BitAbi(lite.use32bitAbi);
+        } catch (IOException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to get path: " + apkFile, e);
+        } finally {
+            IoUtils.closeQuietly(assetLoader);
+        }
+    }
+
+    private static ParsingPackage parseBaseApk(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            DisplayMetrics displayMetrics,
+            File apkFile,
+            AssetManager assets,
+            int flags
+    ) throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+
+        String volumeUuid = null;
+        if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
+            final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
+            volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
+        }
+
+        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+
+        XmlResourceParser parser = null;
+        try {
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+            parser = assets.openXmlResourceParser(cookie, PackageParser.ANDROID_MANIFEST_FILENAME);
+            final Resources res = new Resources(assets, displayMetrics, null);
+
+            ParseResult result = parseBaseApk(parseInput, separateProcesses, callback, apkPath, res,
+                    parser, flags);
+            if (!result.isSuccess()) {
+                throw new PackageParserException(result.getParseError(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + result.getErrorMessage());
+            }
+
+            return result.getResultAndNull()
+                    .setVolumeUuid(volumeUuid)
+                    .setApplicationVolumeUuid(volumeUuid)
+                    .setSigningDetails(SigningDetails.UNKNOWN);
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+        }
+    }
+
+    private static void parseSplitApk(
+            ParseInput parseInput,
+            DisplayMetrics displayMetrics,
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            int splitIndex,
+            AssetManager assets,
+            int flags
+    ) throws PackageParserException {
+        final String apkPath = parsingPackage.getSplitCodePaths()[splitIndex];
+
+        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+        final Resources res;
+        XmlResourceParser parser = null;
+        try {
+            // This must always succeed, as the path has been added to the AssetManager before.
+            final int cookie = assets.findCookieForPath(apkPath);
+            if (cookie == 0) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Failed adding asset path: " + apkPath);
+            }
+
+            parser = assets.openXmlResourceParser(cookie, PackageParser.ANDROID_MANIFEST_FILENAME);
+            res = new Resources(assets, displayMetrics, null);
+
+            final String[] outError = new String[1];
+            ParseResult parseResult = parseSplitApk(parseInput, separateProcesses, parsingPackage,
+                    res, parser, flags, splitIndex, outError);
+            if (!parseResult.isSuccess()) {
+                throw new PackageParserException(parseResult.getParseError(),
+                        apkPath + " (at " + parser.getPositionDescription() + "): "
+                                + parseResult.getErrorMessage());
+            }
+        } catch (PackageParserException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to read manifest from " + apkPath, e);
+        } finally {
+            IoUtils.closeQuietly(parser);
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>base APK</em>. When adding new features you
+     * need to consider whether they should be supported by split APKs and child
+     * packages.
+     *
+     * @param apkPath  The package apk file path
+     * @param res      The resources from which to resolve values
+     * @param parser   The manifest parser
+     * @param flags    Flags how to parse
+     * @return Parsed package or null on error.
+     */
+    private static ParseResult parseBaseApk(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            String apkPath,
+            Resources res,
+            XmlResourceParser parser,
+            int flags
+    ) throws XmlPullParserException, IOException {
+        final String splitName;
+        final String pkgName;
+
+        try {
+            Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
+                    parser);
+            pkgName = packageSplit.first;
+            splitName = packageSplit.second;
+
+            if (!TextUtils.isEmpty(splitName)) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                        "Expected base APK, but found split " + splitName
+                );
+            }
+        } catch (PackageParserException e) {
+            return parseInput.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
+        }
+
+        // TODO: Remove when manifest overlaying removed
+        if (callback != null) {
+            String[] overlayPaths = callback.getOverlayPaths(pkgName, apkPath);
+            if (overlayPaths != null && overlayPaths.length > 0) {
+                for (String overlayPath : overlayPaths) {
+                    res.getAssets().addOverlayPath(overlayPath);
+                }
+            }
+        }
+
+        TypedArray manifestArray = null;
+
+        try {
+            manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
+
+            boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+
+            ParsingPackage parsingPackage = PackageImpl.forParsing(
+                    pkgName,
+                    apkPath,
+                    manifestArray,
+                    isCoreApp
+            );
+
+            ParseResult result = parseBaseApkTags(parseInput, separateProcesses, callback,
+                    parsingPackage, manifestArray, res, parser, flags);
+            if (!result.isSuccess()) {
+                return result;
+            }
+
+            return parseInput.success(parsingPackage);
+        } finally {
+            if (manifestArray != null) {
+                manifestArray.recycle();
+            }
+        }
+    }
+
+    /**
+     * Parse the manifest of a <em>split APK</em>.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     *
+     * @param parsingPackage builder to fill
+     * @return false on failure
+     */
+    private static ParseResult parseSplitApk(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            int flags,
+            int splitIndex,
+            String[] outError
+    ) throws XmlPullParserException, IOException, PackageParserException {
+        AttributeSet attrs = parser;
+
+        // We parsed manifest tag earlier; just skip past it
+        PackageParser.parsePackageSplitNames(parser, attrs);
+
+        int type;
+
+        boolean foundApp = false;
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(PackageParser.TAG_APPLICATION)) {
+                if (foundApp) {
+                    if (PackageParser.RIGID_PARSER) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "<manifest> has more than one <application>"
+                        );
+                    } else {
+                        Slog.w(TAG, "<manifest> has more than one <application>");
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                }
+
+                foundApp = true;
+                ParseResult parseResult = parseSplitApplication(parseInput, separateProcesses,
+                        parsingPackage, res,
+                        parser, flags,
+                        splitIndex, outError);
+                if (!parseResult.isSuccess()) {
+                    return parseResult;
+                }
+
+            } else if (PackageParser.RIGID_PARSER) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Bad element under <manifest>: " + parser.getName()
+                );
+
+            } else {
+                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+                        + " at " + parsingPackage.getBaseCodePath() + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+
+        if (!foundApp) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
+                    "<manifest> does not contain an <application>"
+            );
+        }
+
+        return parseInput.success(parsingPackage);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>split APK</em> manifest.
+     * <p>
+     * Note that split APKs have many more restrictions on what they're capable
+     * of doing, so many valid features of a base APK have been carefully
+     * omitted here.
+     */
+    private static ParseResult parseSplitApplication(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            int flags,
+            int splitIndex,
+            String[] outError
+    ) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+
+        parsingPackage.setSplitHasCode(splitIndex, sa.getBoolean(
+                R.styleable.AndroidManifestApplication_hasCode, true));
+
+        final String classLoaderName = sa.getString(
+                R.styleable.AndroidManifestApplication_classLoader);
+        if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+            parsingPackage.setSplitClassLoaderName(splitIndex, classLoaderName);
+        } else {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Invalid class loader name: " + classLoaderName
+            );
+        }
+
+        final int innerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            ComponentParseUtils.ParsedComponent parsedComponent = null;
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "activity":
+                    ComponentParseUtils.ParsedActivity activity =
+                            ComponentParseUtils.parseActivity(separateProcesses,
+                                    parsingPackage,
+                                    res, parser, flags,
+                                    outError,
+                                    false,
+                                    parsingPackage.isBaseHardwareAccelerated());
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addActivity(activity);
+                    parsedComponent = activity;
+                    break;
+                case "receiver":
+                    activity = ComponentParseUtils.parseActivity(
+                            separateProcesses, parsingPackage,
+                            res, parser, flags, outError,
+                            true, false);
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addReceiver(activity);
+                    parsedComponent = activity;
+                    break;
+                case "service":
+                    ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService(
+                            separateProcesses,
+                            parsingPackage,
+                            res, parser, flags, outError
+                    );
+                    if (s == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addService(s);
+                    parsedComponent = s;
+                    break;
+                case "provider":
+                    ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider(
+                            separateProcesses,
+                            parsingPackage,
+                            res, parser, flags, outError);
+                    if (p == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addProvider(p);
+                    parsedComponent = p;
+                    break;
+                case "activity-alias":
+                    activity = ComponentParseUtils.parseActivityAlias(
+                            parsingPackage,
+                            res,
+                            parser,
+                            outError
+                    );
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addActivity(activity);
+                    parsedComponent = activity;
+                    break;
+                case "meta-data":
+                    // note: application meta-data is stored off to the side, so it can
+                    // remain null in the primary copy (we like to avoid extra copies because
+                    // it can be large)
+                    Bundle appMetaData = parseMetaData(parsingPackage, res, parser,
+                            parsingPackage.getAppMetaData(),
+                            outError);
+                    if (appMetaData == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.setAppMetaData(appMetaData);
+                    break;
+                case "uses-static-library":
+                    ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage,
+                            res, parser);
+                    if (!parseResult.isSuccess()) {
+                        return parseResult;
+                    }
+
+                    break;
+                case "uses-library":
+                    sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
+
+                    // Note: don't allow this value to be a reference to a resource
+                    // that may change.
+                    String lname = sa.getNonResourceString(
+                            R.styleable.AndroidManifestUsesLibrary_name);
+                    boolean req = sa.getBoolean(
+                            R.styleable.AndroidManifestUsesLibrary_required, true);
+
+                    sa.recycle();
+
+                    if (lname != null) {
+                        lname = lname.intern();
+                        if (req) {
+                            // Upgrade to treat as stronger constraint
+                            parsingPackage.addUsesLibrary(lname)
+                                    .removeUsesOptionalLibrary(lname);
+                        } else {
+                            // Ignore if someone already defined as required
+                            if (!ArrayUtils.contains(parsingPackage.getUsesLibraries(), lname)) {
+                                parsingPackage.addUsesOptionalLibrary(lname);
+                            }
+                        }
+                    }
+
+                    XmlUtils.skipCurrentTag(parser);
+                    break;
+                case "uses-package":
+                    // Dependencies for app installers; we don't currently try to
+                    // enforce this.
+                    XmlUtils.skipCurrentTag(parser);
+                    break;
+                default:
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG, "Unknown element under <application>: " + tagName
+                                + " at " + parsingPackage.getBaseCodePath() + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Bad element under <application>: " + tagName
+                        );
+                    }
+            }
+
+            if (parsedComponent != null && parsedComponent.getSplitName() == null) {
+                // If the loaded component did not specify a split, inherit the split name
+                // based on the split it is defined in.
+                // This is used to later load the correct split when starting this
+                // component.
+                parsedComponent.setSplitName(parsingPackage.getSplitNames()[splitIndex]);
+            }
+        }
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parseBaseApkTags(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            ParsingPackage parsingPackage,
+            TypedArray manifestArray,
+            Resources res,
+            XmlResourceParser parser,
+            int flags
+    ) throws XmlPullParserException, IOException {
+        int type;
+        boolean foundApp = false;
+
+        TypedArray sa = manifestArray;
+
+        ParseResult sharedUserResult = parseSharedUser(parseInput, parsingPackage, sa);
+        if (!sharedUserResult.isSuccess()) {
+            return sharedUserResult;
+        }
+
+        parseManifestAttributes(sa, parsingPackage, flags);
+
+        int outerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+
+            // All methods return a boolean, even if they can't fail. This can be enforced
+            // by making this final and not assigned, forcing the switch to assign success
+            // once in every branch.
+            final boolean success;
+            ParseResult parseResult = null;
+
+            // TODO(b/135203078): Either use all booleans or all ParseResults
+            // TODO(b/135203078): Convert to instance methods to share variables
+            switch (tagName) {
+                case PackageParser.TAG_APPLICATION:
+                    if (foundApp) {
+                        if (PackageParser.RIGID_PARSER) {
+                            return parseInput.error(
+                                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                    "<manifest> has more than one <application>"
+                            );
+                        } else {
+                            Slog.w(TAG, "<manifest> has more than one <application>");
+                            XmlUtils.skipCurrentTag(parser);
+                            success = true;
+                        }
+                    } else {
+                        foundApp = true;
+                        parseResult = parseBaseApplication(parseInput, separateProcesses,
+                                callback,
+                                parsingPackage, res, parser, flags);
+                        success = parseResult.isSuccess();
+                    }
+                    break;
+                case PackageParser.TAG_OVERLAY:
+                    parseResult = parseOverlay(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_KEY_SETS:
+                    parseResult = parseKeySets(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_PERMISSION_GROUP:
+                    parseResult = parsePermissionGroup(parseInput, parsingPackage, res,
+                            parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_PERMISSION:
+                    parseResult = parsePermission(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_PERMISSION_TREE:
+                    parseResult = parsePermissionTree(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_USES_PERMISSION:
+                case PackageParser.TAG_USES_PERMISSION_SDK_M:
+                case PackageParser.TAG_USES_PERMISSION_SDK_23:
+                    parseResult = parseUsesPermission(parseInput, parsingPackage, res, parser,
+                            callback);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_USES_CONFIGURATION:
+                    success = parseUsesConfiguration(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_USES_FEATURE:
+                    success = parseUsesFeature(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_FEATURE_GROUP:
+                    success = parseFeatureGroup(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_USES_SDK:
+                    parseResult = parseUsesSdk(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_SUPPORT_SCREENS:
+                    success = parseSupportScreens(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_PROTECTED_BROADCAST:
+                    success = parseProtectedBroadcast(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_INSTRUMENTATION:
+                    parseResult = parseInstrumentation(parseInput, parsingPackage, res,
+                            parser);
+                    success = parseResult.isSuccess();
+                    break;
+                case PackageParser.TAG_ORIGINAL_PACKAGE:
+                    success = parseOriginalPackage(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_ADOPT_PERMISSIONS:
+                    success = parseAdoptPermissions(parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_USES_GL_TEXTURE:
+                case PackageParser.TAG_COMPATIBLE_SCREENS:
+                case PackageParser.TAG_SUPPORTS_INPUT:
+                case PackageParser.TAG_EAT_COMMENT:
+                    // Just skip this tag
+                    XmlUtils.skipCurrentTag(parser);
+                    success = true;
+                    break;
+                case PackageParser.TAG_RESTRICT_UPDATE:
+                    success = parseRestrictUpdateHash(flags, parsingPackage, res, parser);
+                    break;
+                case PackageParser.TAG_QUERIES:
+                    parseResult = parseQueries(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
+                default:
+                    parseResult = parseUnknownTag(parseInput, parsingPackage, parser);
+                    success = parseResult.isSuccess();
+                    break;
+            }
+
+            if (parseResult != null && !parseResult.isSuccess()) {
+                return parseResult;
+            }
+
+            if (!success) {
+                return parseResult;
+            }
+        }
+
+        if (!foundApp && ArrayUtils.size(parsingPackage.getInstrumentations()) == 0) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
+                    "<manifest> does not contain an <application> or <instrumentation>"
+            );
+        }
+
+        convertNewPermissions(parsingPackage);
+
+        convertSplitPermissions(parsingPackage);
+
+        // At this point we can check if an application is not supporting densities and hence
+        // cannot be windowed / resized. Note that an SDK version of 0 is common for
+        // pre-Doughnut applications.
+        if (parsingPackage.usesCompatibilityMode()) {
+            adjustPackageToBeUnresizeableAndUnpipable(parsingPackage);
+        }
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parseUnknownTag(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        if (PackageParser.RIGID_PARSER) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Bad element under <manifest>: " + parser.getName()
+            );
+        } else {
+            Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+                    + " at " + parsingPackage.getBaseCodePath() + " "
+                    + parser.getPositionDescription());
+            XmlUtils.skipCurrentTag(parser);
+            return parseInput.success(parsingPackage);
+        }
+    }
+
+    private static ParseResult parseSharedUser(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            TypedArray manifestArray
+    ) {
+        String str = manifestArray.getNonConfigurationString(
+                R.styleable.AndroidManifest_sharedUserId, 0);
+        if (TextUtils.isEmpty(str)) {
+            return parseInput.success(parsingPackage);
+        }
+
+        String nameError = validateName(str, true, true);
+        if (nameError != null && !"android".equals(parsingPackage.getPackageName())) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                    "<manifest> specifies bad sharedUserId name \"" + str + "\": "
+                            + nameError
+            );
+        }
+
+        int sharedUserLabel = manifestArray.getResourceId(
+                R.styleable.AndroidManifest_sharedUserLabel, 0);
+        parsingPackage.setSharedUserId(str.intern())
+                .setSharedUserLabel(sharedUserLabel);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static void parseManifestAttributes(
+            TypedArray manifestArray,
+            ParsingPackage parsingPackage,
+            int flags
+    ) {
+        int installLocation = manifestArray.getInteger(R.styleable.AndroidManifest_installLocation,
+                PackageParser.PARSE_DEFAULT_INSTALL_LOCATION);
+
+        final int targetSandboxVersion = manifestArray.getInteger(
+                R.styleable.AndroidManifest_targetSandboxVersion,
+                PackageParser.PARSE_DEFAULT_TARGET_SANDBOX);
+
+        parsingPackage.setInstallLocation(installLocation)
+                .setTargetSandboxVersion(targetSandboxVersion);
+
+        /* Set the global "on SD card" flag */
+        parsingPackage.setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
+
+        parsingPackage.setIsolatedSplitLoading(manifestArray.getBoolean(
+                R.styleable.AndroidManifest_isolatedSplits, false));
+    }
+
+    private static ParseResult parseKeySets(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        // we've encountered the 'key-sets' tag
+        // all the keys and keysets that we want must be defined here
+        // so we're going to iterate over the parser and pull out the things we want
+        int outerDepth = parser.getDepth();
+        int currentKeySetDepth = -1;
+        int type;
+        String currentKeySet = null;
+        ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
+        ArraySet<String> upgradeKeySets = new ArraySet<>();
+        ArrayMap<String, ArraySet<String>> definedKeySets =
+                new ArrayMap<>();
+        ArraySet<String> improperKeySets = new ArraySet<>();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG) {
+                if (parser.getDepth() == currentKeySetDepth) {
+                    currentKeySet = null;
+                    currentKeySetDepth = -1;
+                }
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals("key-set")) {
+                if (currentKeySet != null) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Improperly nested 'key-set' tag at " + parser.getPositionDescription()
+                    );
+                }
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestKeySet);
+                final String keysetName = sa.getNonResourceString(
+                        R.styleable.AndroidManifestKeySet_name);
+                definedKeySets.put(keysetName, new ArraySet<>());
+                currentKeySet = keysetName;
+                currentKeySetDepth = parser.getDepth();
+                sa.recycle();
+            } else if (tagName.equals("public-key")) {
+                if (currentKeySet == null) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Improperly nested 'key-set' tag at " + parser.getPositionDescription()
+                    );
+                }
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestPublicKey);
+                final String publicKeyName = sa.getNonResourceString(
+                        R.styleable.AndroidManifestPublicKey_name);
+                final String encodedKey = sa.getNonResourceString(
+                        R.styleable.AndroidManifestPublicKey_value);
+                if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
+                    sa.recycle();
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "'public-key' " + publicKeyName + " must define a public-key value"
+                                    + " on first use at " + parser.getPositionDescription()
+                    );
+                } else if (encodedKey != null) {
+                    PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
+                    if (currentKey == null) {
+                        Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+                                + parser.getPositionDescription() + " key-set " + currentKeySet
+                                + " will not be added to the package's defined key-sets.");
+                        sa.recycle();
+                        improperKeySets.add(currentKeySet);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    if (publicKeys.get(publicKeyName) == null
+                            || publicKeys.get(publicKeyName).equals(currentKey)) {
+
+                        /* public-key first definition, or matches old definition */
+                        publicKeys.put(publicKeyName, currentKey);
+                    } else {
+                        sa.recycle();
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Value of 'public-key' " + publicKeyName
+                                        + " conflicts with previously defined value at "
+                                        + parser.getPositionDescription()
+                        );
+                    }
+                }
+                definedKeySets.get(currentKeySet).add(publicKeyName);
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("upgrade-key-set")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestUpgradeKeySet);
+                String name = sa.getNonResourceString(
+                        R.styleable.AndroidManifestUpgradeKeySet_name);
+                upgradeKeySets.add(name);
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (PackageParser.RIGID_PARSER) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Bad element under <key-sets>: " + parser.getName()
+                                + " at " + parsingPackage.getBaseCodePath() + " "
+                                + parser.getPositionDescription()
+                );
+            } else {
+                Slog.w(TAG, "Unknown element under <key-sets>: " + parser.getName()
+                        + " at " + parsingPackage.getBaseCodePath() + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+            }
+        }
+        String packageName = parsingPackage.getPackageName();
+        Set<String> publicKeyNames = publicKeys.keySet();
+        if (publicKeyNames.removeAll(definedKeySets.keySet())) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Package" + packageName + " AndroidManifest.xml "
+                            + "'key-set' and 'public-key' names must be distinct."
+            );
+        }
+
+        for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
+            final String keySetName = e.getKey();
+            if (e.getValue().size() == 0) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " has no valid associated 'public-key'."
+                        + " Not including in package's defined key-sets.");
+                continue;
+            } else if (improperKeySets.contains(keySetName)) {
+                Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+                        + "'key-set' " + keySetName + " contained improper 'public-key'"
+                        + " tags. Not including in package's defined key-sets.");
+                continue;
+            }
+
+            for (String s : e.getValue()) {
+                parsingPackage.addKeySet(keySetName, publicKeys.get(s));
+            }
+        }
+        if (parsingPackage.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
+            parsingPackage.setUpgradeKeySets(upgradeKeySets);
+        } else {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Package" + packageName + " AndroidManifest.xml "
+                            + "does not define all 'upgrade-key-set's ."
+            );
+        }
+
+        return parseInput.success(parsingPackage);
+    }
+
+    public static boolean parsePackageItemInfo(String packageName, PackageItemInfo outInfo,
+            String[] outError, String tag, TypedArray sa, boolean nameRequired,
+            int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
+        // This case can only happen in unit tests where we sometimes need to create fakes
+        // of various package parser data structures.
+        if (sa == null) {
+            outError[0] = tag + " does not contain any attributes";
+            return false;
+        }
+
+        String name = sa.getNonConfigurationString(nameRes, 0);
+        if (name == null) {
+            if (nameRequired) {
+                outError[0] = tag + " does not specify android:name";
+                return false;
+            }
+        } else {
+            String outInfoName = buildClassName(packageName, name);
+            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+                outError[0] = tag + " invalid android:name";
+                return false;
+            }
+            outInfo.name = outInfoName;
+            if (outInfoName == null) {
+                return false;
+            }
+        }
+
+        int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
+        if (roundIconVal != 0) {
+            outInfo.icon = roundIconVal;
+            outInfo.nonLocalizedLabel = null;
+        } else {
+            int iconVal = sa.getResourceId(iconRes, 0);
+            if (iconVal != 0) {
+                outInfo.icon = iconVal;
+                outInfo.nonLocalizedLabel = null;
+            }
+        }
+
+        int logoVal = sa.getResourceId(logoRes, 0);
+        if (logoVal != 0) {
+            outInfo.logo = logoVal;
+        }
+
+        int bannerVal = sa.getResourceId(bannerRes, 0);
+        if (bannerVal != 0) {
+            outInfo.banner = bannerVal;
+        }
+
+        TypedValue v = sa.peekValue(labelRes);
+        if (v != null && (outInfo.labelRes = v.resourceId) == 0) {
+            outInfo.nonLocalizedLabel = v.coerceToString();
+        }
+
+        outInfo.packageName = packageName;
+
+        return true;
+    }
+
+    private static ParseResult parsePackageItemInfo(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            String tag,
+            TypedArray sa,
+            boolean nameRequired,
+            int nameRes,
+            int labelRes,
+            int iconRes,
+            int roundIconRes,
+            int logoRes,
+            int bannerRes
+    ) {
+        // This case can only happen in unit tests where we sometimes need to create fakes
+        // of various package parser data structures.
+        if (sa == null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    tag + " does not contain any attributes"
+            );
+        }
+
+        String name = sa.getNonConfigurationString(nameRes, 0);
+        if (name == null) {
+            if (nameRequired) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        tag + " does not specify android:name"
+                );
+            }
+        } else {
+            String packageName = parsingPackage.getPackageName();
+            String outInfoName = buildClassName(packageName, name);
+            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        tag + " invalid android:name"
+                );
+            } else if (outInfoName == null) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Empty class name in package " + packageName
+                );
+            }
+
+            parsingPackage.setName(outInfoName);
+        }
+
+        int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
+        if (roundIconVal != 0) {
+            parsingPackage.setIcon(roundIconVal)
+                    .setNonLocalizedLabel(null);
+        } else {
+            int iconVal = sa.getResourceId(iconRes, 0);
+            if (iconVal != 0) {
+                parsingPackage.setIcon(iconVal)
+                        .setNonLocalizedLabel(null);
+            }
+        }
+
+        int logoVal = sa.getResourceId(logoRes, 0);
+        if (logoVal != 0) {
+            parsingPackage.setLogo(logoVal);
+        }
+
+        int bannerVal = sa.getResourceId(bannerRes, 0);
+        if (bannerVal != 0) {
+            parsingPackage.setBanner(bannerVal);
+        }
+
+        TypedValue v = sa.peekValue(labelRes);
+        if (v != null) {
+            parsingPackage.setLabelRes(v.resourceId);
+            if (v.resourceId == 0) {
+                parsingPackage.setNonLocalizedLabel(v.coerceToString());
+            }
+        }
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parsePermissionGroup(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+
+        ComponentParseUtils.ParsedPermissionGroup parsedPermissionGroup =
+                ComponentParseUtils.parsePermissionGroup(parsingPackage,
+                        res, parser, outError);
+
+        if (parsedPermissionGroup == null || outError[0] != null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    outError[0]
+            );
+        }
+
+        parsingPackage.addPermissionGroup(parsedPermissionGroup);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parsePermission(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+
+        ComponentParseUtils.ParsedPermission parsedPermission =
+                ComponentParseUtils.parsePermission(parsingPackage,
+                        res, parser, outError);
+
+        if (parsedPermission == null || outError[0] != null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    outError[0]
+            );
+        }
+
+        parsingPackage.addPermission(parsedPermission);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parsePermissionTree(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+
+        ComponentParseUtils.ParsedPermission parsedPermission =
+                ComponentParseUtils.parsePermissionTree(parsingPackage,
+                        res, parser, outError);
+
+        if (parsedPermission == null || outError[0] != null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    outError[0]
+            );
+        }
+
+        parsingPackage.addPermission(parsedPermission);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parseUsesPermission(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            PackageParser.Callback callback
+    )
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestUsesPermission);
+
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        String name = sa.getNonResourceString(
+                R.styleable.AndroidManifestUsesPermission_name);
+
+        int maxSdkVersion = 0;
+        TypedValue val = sa.peekValue(
+                R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
+        if (val != null) {
+            if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
+                maxSdkVersion = val.data;
+            }
+        }
+
+        final String requiredFeature = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+        final String requiredNotfeature = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
+                0);
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+
+        // Can only succeed from here on out
+        ParseResult success = parseInput.success(parsingPackage);
+
+        if (name == null) {
+            return success;
+        }
+
+        if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+            return success;
+        }
+
+        // Only allow requesting this permission if the platform supports the given feature.
+        if (requiredFeature != null && callback != null && !callback.hasFeature(requiredFeature)) {
+            return success;
+        }
+
+        // Only allow requesting this permission if the platform doesn't support the given feature.
+        if (requiredNotfeature != null && callback != null
+                && callback.hasFeature(requiredNotfeature)) {
+            return success;
+        }
+
+        if (!parsingPackage.getRequestedPermissions().contains(name)) {
+            parsingPackage.addRequestedPermission(name.intern());
+        } else {
+            Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+                    + name + " in package: " + parsingPackage.getPackageName() + " at: "
+                    + parser.getPositionDescription());
+        }
+
+        return success;
+    }
+
+    private static boolean parseUsesConfiguration(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        ConfigurationInfo cPref = new ConfigurationInfo();
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestUsesConfiguration);
+        cPref.reqTouchScreen = sa.getInt(
+                R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
+                Configuration.TOUCHSCREEN_UNDEFINED);
+        cPref.reqKeyboardType = sa.getInt(
+                R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
+                Configuration.KEYBOARD_UNDEFINED);
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
+                false)) {
+            cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+        }
+        cPref.reqNavigation = sa.getInt(
+                R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
+                Configuration.NAVIGATION_UNDEFINED);
+        if (sa.getBoolean(
+                R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
+                false)) {
+            cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+        }
+        sa.recycle();
+        parsingPackage.addConfigPreference(cPref);
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static boolean parseUsesFeature(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        FeatureInfo fi = parseFeatureInfo(res, parser);
+        parsingPackage.addReqFeature(fi);
+
+        if (fi.name == null) {
+            ConfigurationInfo cPref = new ConfigurationInfo();
+            cPref.reqGlEsVersion = fi.reqGlEsVersion;
+            parsingPackage.addConfigPreference(cPref);
+        }
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
+        FeatureInfo fi = new FeatureInfo();
+        TypedArray sa = res.obtainAttributes(attrs,
+                R.styleable.AndroidManifestUsesFeature);
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
+        fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
+        if (fi.name == null) {
+            fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
+                    FeatureInfo.GL_ES_VERSION_UNDEFINED);
+        }
+        if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
+            fi.flags |= FeatureInfo.FLAG_REQUIRED;
+        }
+        sa.recycle();
+        return fi;
+    }
+
+    private static boolean parseFeatureGroup(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        FeatureGroupInfo group = new FeatureGroupInfo();
+        ArrayList<FeatureInfo> features = null;
+        final int innerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            final String innerTagName = parser.getName();
+            if (innerTagName.equals("uses-feature")) {
+                FeatureInfo featureInfo = parseFeatureInfo(res, parser);
+                // FeatureGroups are stricter and mandate that
+                // any <uses-feature> declared are mandatory.
+                featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+                features = ArrayUtils.add(features, featureInfo);
+            } else {
+                Slog.w(TAG,
+                        "Unknown element under <feature-group>: " + innerTagName +
+                                " at " + parsingPackage.getBaseCodePath() + " " +
+                                parser.getPositionDescription());
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+
+        if (features != null) {
+            group.features = new FeatureInfo[features.size()];
+            group.features = features.toArray(group.features);
+        }
+
+        parsingPackage.addFeatureGroup(group);
+        return true;
+    }
+
+    private static ParseResult parseUsesSdk(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        if (PackageParser.SDK_VERSION > 0) {
+            TypedArray sa = res.obtainAttributes(parser,
+                    R.styleable.AndroidManifestUsesSdk);
+
+            int minVers = 1;
+            String minCode = null;
+            int targetVers = 0;
+            String targetCode = null;
+
+            TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+            if (val != null) {
+                if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                    minCode = val.string.toString();
+                } else {
+                    // If it's not a string, it's an integer.
+                    minVers = val.data;
+                }
+            }
+
+            val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+            if (val != null) {
+                if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+                    targetCode = val.string.toString();
+                    if (minCode == null) {
+                        minCode = targetCode;
+                    }
+                } else {
+                    // If it's not a string, it's an integer.
+                    targetVers = val.data;
+                }
+            } else {
+                targetVers = minVers;
+                targetCode = minCode;
+            }
+
+            sa.recycle();
+
+            // TODO(b/135203078): Remove, replace with ParseResult
+            String[] outError = new String[1];
+            final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers,
+                    minCode,
+                    PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, outError);
+            if (minSdkVersion < 0) {
+                return parseInput.error(
+                        PackageManager.INSTALL_FAILED_OLDER_SDK
+                );
+            }
+
+            final int targetSdkVersion = PackageParser.computeTargetSdkVersion(
+                    targetVers,
+                    targetCode, PackageParser.SDK_CODENAMES, outError);
+            if (targetSdkVersion < 0) {
+                return parseInput.error(
+                        PackageManager.INSTALL_FAILED_OLDER_SDK
+                );
+            }
+
+            parsingPackage.setMinSdkVersion(minSdkVersion)
+                    .setTargetSdkVersion(targetSdkVersion);
+        }
+
+        XmlUtils.skipCurrentTag(parser);
+        return parseInput.success(parsingPackage);
+    }
+
+    private static boolean parseRestrictUpdateHash(
+            int flags,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+            TypedArray sa = res.obtainAttributes(parser,
+                    R.styleable.AndroidManifestRestrictUpdate);
+            final String hash = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestRestrictUpdate_hash,
+                    0);
+            sa.recycle();
+
+            if (hash != null) {
+                final int hashLength = hash.length();
+                final byte[] hashBytes = new byte[hashLength / 2];
+                for (int i = 0; i < hashLength; i += 2) {
+                    hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
+                            << 4)
+                            + Character.digit(hash.charAt(i + 1), 16));
+                }
+                parsingPackage.setRestrictUpdateHash(hashBytes);
+            } else {
+                parsingPackage.setRestrictUpdateHash(null);
+            }
+        }
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static ParseResult parseQueries(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (parser.getName().equals("intent")) {
+                String[] outError = new String[1];
+                ComponentParseUtils.ParsedQueriesIntentInfo intentInfo =
+                        ComponentParseUtils.parsedParsedQueriesIntentInfo(
+                                parsingPackage, res, parser, outError
+                        );
+                if (intentInfo == null) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+
+                Uri data = null;
+                String dataType = null;
+                String host = "";
+                final int numActions = intentInfo.countActions();
+                final int numSchemes = intentInfo.countDataSchemes();
+                final int numTypes = intentInfo.countDataTypes();
+                final int numHosts = intentInfo.getHosts().length;
+                if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
+                    outError[0] = "intent tags must contain either an action or data.";
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+                if (numActions > 1) {
+                    outError[0] = "intent tag may have at most one action.";
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+                if (numTypes > 1) {
+                    outError[0] = "intent tag may have at most one data type.";
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+                if (numSchemes > 1) {
+                    outError[0] = "intent tag may have at most one data scheme.";
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+                if (numHosts > 1) {
+                    outError[0] = "intent tag may have at most one data host.";
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            outError[0]
+                    );
+                }
+                Intent intent = new Intent();
+                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+                    intent.addCategory(intentInfo.getCategory(i));
+                }
+                if (numHosts == 1) {
+                    host = intentInfo.getHosts()[0];
+                }
+                if (numSchemes == 1) {
+                    data = new Uri.Builder()
+                            .scheme(intentInfo.getDataScheme(0))
+                            .authority(host)
+                            .build();
+                }
+                if (numTypes == 1) {
+                    dataType = intentInfo.getDataType(0);
+                }
+                intent.setDataAndType(data, dataType);
+                if (numActions == 1) {
+                    intent.setAction(intentInfo.getAction(0));
+                }
+                parsingPackage.addQueriesIntent(intent);
+            } else if (parser.getName().equals("package")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesPackage);
+                final String packageName =
+                        sa.getString(R.styleable.AndroidManifestQueriesPackage_name);
+                if (TextUtils.isEmpty(packageName)) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Package name is missing from package tag."
+                    );
+                }
+                parsingPackage.addQueriesPackage(packageName.intern());
+            }
+        }
+        return parseInput.success(parsingPackage);
+    }
+
+    /**
+     * Parse the {@code application} XML tree at the current parse location in a
+     * <em>base APK</em> manifest.
+     * <p>
+     * When adding new features, carefully consider if they should also be
+     * supported by split APKs.
+     *
+     * @hide
+     */
+    public static ParseResult parseBaseApplication(
+            ParseInput parseInput,
+            String[] separateProcesses,
+            PackageParser.Callback callback,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            int flags
+    ) throws XmlPullParserException, IOException {
+        final String pkgName = parsingPackage.getPackageName();
+
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+        TypedArray sa = null;
+
+        try {
+            sa = res.obtainAttributes(parser,
+                    R.styleable.AndroidManifestApplication);
+
+
+            parsingPackage
+                    .setIconRes(
+                            sa.getResourceId(R.styleable.AndroidManifestApplication_icon, 0))
+                    .setRoundIconRes(
+                            sa.getResourceId(R.styleable.AndroidManifestApplication_roundIcon, 0));
+
+            ParseResult result = parsePackageItemInfo(
+                    parseInput,
+                    parsingPackage,
+                    "<application>",
+                    sa, false /*nameRequired*/,
+                    R.styleable.AndroidManifestApplication_name,
+                    R.styleable.AndroidManifestApplication_label,
+                    R.styleable.AndroidManifestApplication_icon,
+                    R.styleable.AndroidManifestApplication_roundIcon,
+                    R.styleable.AndroidManifestApplication_logo,
+                    R.styleable.AndroidManifestApplication_banner
+            );
+            if (!result.isSuccess()) {
+                return result;
+            }
+
+            String name = parsingPackage.getName();
+            if (name != null) {
+                parsingPackage.setClassName(name);
+            }
+
+            String manageSpaceActivity = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestApplication_manageSpaceActivity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            if (manageSpaceActivity != null) {
+                String manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity);
+
+                if (manageSpaceActivityName == null) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Empty class name in package " + pkgName
+                    );
+                }
+
+                parsingPackage.setManageSpaceActivityName(manageSpaceActivityName);
+            }
+
+            boolean allowBackup = sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowBackup, true);
+            parsingPackage.setAllowBackup(allowBackup);
+
+            if (allowBackup) {
+                // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
+                // and restoreAnyVersion are only relevant if backup is possible for the
+                // given application.
+                String backupAgent = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_backupAgent,
+                        Configuration.NATIVE_CONFIG_VERSION);
+                if (backupAgent != null) {
+                    String backupAgentName = buildClassName(pkgName, backupAgent);
+                    if (backupAgentName == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Empty class name in package " + pkgName
+                        );
+                    }
+
+                    if (PackageParser.DEBUG_BACKUP) {
+                        Slog.v(TAG, "android:backupAgent = " + backupAgentName
+                                + " from " + pkgName + "+" + backupAgent);
+                    }
+
+                    parsingPackage.setBackupAgentName(backupAgentName);
+
+                    parsingPackage.setKillAfterRestore(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_killAfterRestore, true));
+
+                    parsingPackage.setRestoreAnyVersion(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_restoreAnyVersion, false));
+
+                    parsingPackage.setFullBackupOnly(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_fullBackupOnly, false));
+
+                    parsingPackage.setBackupInForeground(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_backupInForeground,
+                            false));
+                }
+
+                TypedValue v = sa.peekValue(
+                        R.styleable.AndroidManifestApplication_fullBackupContent);
+                int fullBackupContent = 0;
+
+                if (v != null) {
+                    fullBackupContent = v.resourceId;
+
+                    if (v.resourceId == 0) {
+                        if (PackageParser.DEBUG_BACKUP) {
+                            Slog.v(TAG, "fullBackupContent specified as boolean=" +
+                                    (v.data == 0 ? "false" : "true"));
+                        }
+                        // "false" => -1, "true" => 0
+                        fullBackupContent = v.data == 0 ? -1 : 0;
+                    }
+
+                    parsingPackage.setFullBackupContent(fullBackupContent);
+                }
+                if (PackageParser.DEBUG_BACKUP) {
+                    Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
+                }
+            }
+
+            parsingPackage
+                    .setTheme(
+                            sa.getResourceId(R.styleable.AndroidManifestApplication_theme, 0))
+                    .setDescriptionRes(
+                            sa.getResourceId(R.styleable.AndroidManifestApplication_description,
+                                    0));
+
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_persistent,
+                    false)) {
+                // Check if persistence is based on a feature being present
+                final String requiredFeature = sa.getNonResourceString(R.styleable
+                        .AndroidManifestApplication_persistentWhenFeatureAvailable);
+                parsingPackage.setPersistent(requiredFeature == null
+                        || callback.hasFeature(requiredFeature));
+            }
+
+            boolean requiredForAllUsers = sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_requiredForAllUsers,
+                    false);
+            parsingPackage.setRequiredForAllUsers(requiredForAllUsers);
+
+            String restrictedAccountType = sa.getString(R.styleable
+                    .AndroidManifestApplication_restrictedAccountType);
+            if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
+                parsingPackage.setRestrictedAccountType(restrictedAccountType);
+            }
+
+            String requiredAccountType = sa.getString(R.styleable
+                    .AndroidManifestApplication_requiredAccountType);
+            if (requiredAccountType != null && requiredAccountType.length() > 0) {
+                parsingPackage.setRequiredAccountType(requiredAccountType);
+            }
+
+            parsingPackage.setForceQueryable(
+                    sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false)
+            );
+
+            boolean debuggable = sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_debuggable,
+                    false
+            );
+
+            parsingPackage.setDebuggable(debuggable);
+
+            if (debuggable) {
+                // Debuggable implies profileable
+                parsingPackage.setProfileableByShell(true);
+            }
+
+            parsingPackage.setVmSafeMode(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_vmSafeMode, false));
+
+            boolean baseHardwareAccelerated = sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_hardwareAccelerated,
+                    parsingPackage.getTargetSdkVersion()
+                            >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
+            parsingPackage.setBaseHardwareAccelerated(baseHardwareAccelerated);
+
+            parsingPackage.setHasCode(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_hasCode, true));
+
+            parsingPackage.setAllowTaskReparenting(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowTaskReparenting, false));
+
+            parsingPackage.setAllowClearUserData(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowClearUserData, true));
+
+            parsingPackage.setTestOnly(sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
+                    false));
+
+            parsingPackage.setLargeHeap(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_largeHeap, false));
+
+            parsingPackage.setUsesCleartextTraffic(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_usesCleartextTraffic,
+                    parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.P));
+
+            parsingPackage.setSupportsRtl(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_supportsRtl,
+                    false /* default is no RTL support*/));
+
+            parsingPackage.setMultiArch(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_multiArch, false));
+
+            parsingPackage.setExtractNativeLibs(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_extractNativeLibs, true));
+
+            parsingPackage.setUseEmbeddedDex(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_useEmbeddedDex, false));
+
+            parsingPackage.setDefaultToDeviceProtectedStorage(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
+                    false));
+
+            parsingPackage.setDirectBootAware(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_directBootAware, false));
+
+            if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
+                parsingPackage.setActivitiesResizeModeResizeable(sa.getBoolean(
+                        R.styleable.AndroidManifestApplication_resizeableActivity, true));
+            } else {
+                parsingPackage.setActivitiesResizeModeResizeableViaSdkVersion(
+                        parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.N);
+            }
+
+            parsingPackage.setAllowClearUserDataOnFailedRestore(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore,
+                    true));
+
+
+            parsingPackage.setAllowAudioPlaybackCapture(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture,
+                    parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q));
+
+            parsingPackage.setRequestLegacyExternalStorage(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
+                    parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q));
+
+            parsingPackage
+                    .setMaxAspectRatio(
+                            sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
+                    .setMinAspectRatio(
+                            sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0))
+                    .setNetworkSecurityConfigRes(sa.getResourceId(
+                            R.styleable.AndroidManifestApplication_networkSecurityConfig, 0))
+                    .setCategory(sa.getInt(R.styleable.AndroidManifestApplication_appCategory,
+                            ApplicationInfo.CATEGORY_UNDEFINED));
+
+            String str;
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestApplication_permission, 0);
+            parsingPackage.setPermission((str != null && str.length() > 0) ? str.intern() : null);
+
+            if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_taskAffinity,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                str = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_taskAffinity);
+            }
+            String packageName = parsingPackage.getPackageName();
+            String taskAffinity = PackageParser.buildTaskAffinityName(packageName,
+                    packageName,
+                    str, outError);
+
+            if (outError[0] != null) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        outError[0]
+                );
+            }
+
+            parsingPackage.setTaskAffinity(taskAffinity);
+            String factory = sa.getNonResourceString(
+                    R.styleable.AndroidManifestApplication_appComponentFactory);
+            if (factory != null) {
+                String appComponentFactory = buildClassName(packageName, factory);
+                if (appComponentFactory == null) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Empty class name in package " + pkgName
+                    );
+                }
+
+                parsingPackage.setAppComponentFactory(appComponentFactory);
+            }
+
+            parsingPackage.setUsesNonSdkApi(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_usesNonSdkApi, false));
+
+            parsingPackage.setHasFragileUserData(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_hasFragileUserData, false));
+
+            CharSequence pname;
+            if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestApplication_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(
+                        R.styleable.AndroidManifestApplication_process);
+            }
+            String processName = PackageParser.buildProcessName(packageName, null, pname, flags,
+                    separateProcesses, outError);
+
+            if (outError[0] != null) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        outError[0]
+                );
+            }
+
+            parsingPackage
+                    .setProcessName(processName)
+                    .setEnabled(
+                            sa.getBoolean(R.styleable.AndroidManifestApplication_enabled,
+                                    true));
+
+            parsingPackage.setIsGame(sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_isGame, false));
+
+            boolean cantSaveState = sa.getBoolean(
+                    R.styleable.AndroidManifestApplication_cantSaveState, false);
+            parsingPackage.setCantSaveState(cantSaveState);
+            if (cantSaveState) {
+                // A heavy-weight application can not be in a custom process.
+                // We can do direct compare because we intern all strings.
+                if (processName != null && !processName.equals(packageName)) {
+                    return parseInput.error(
+                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "cantSaveState applications can not use custom processes"
+                    );
+                }
+            }
+
+            String classLoaderName = sa.getString(
+                    R.styleable.AndroidManifestApplication_classLoader);
+            parsingPackage
+                    .setUiOptions(sa.getInt(R.styleable.AndroidManifestApplication_uiOptions, 0))
+                    .setClassLoaderName(classLoaderName)
+                    .setZygotePreloadName(
+                            sa.getString(R.styleable.AndroidManifestApplication_zygotePreloadName));
+
+            if (classLoaderName != null
+                    && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        "Invalid class loader name: " + classLoaderName
+                );
+            }
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        final int innerDepth = parser.getDepth();
+        int type;
+        boolean hasActivityOrder = false;
+        boolean hasReceiverOrder = false;
+        boolean hasServiceOrder = false;
+
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            switch (tagName) {
+                case "activity":
+                    ComponentParseUtils.ParsedActivity activity =
+                            ComponentParseUtils.parseActivity(separateProcesses,
+                                    parsingPackage,
+                                    res, parser, flags,
+                                    outError, false,
+                                    parsingPackage.isBaseHardwareAccelerated());
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    hasActivityOrder |= (activity.order != 0);
+                    parsingPackage.addActivity(activity);
+                    break;
+                case "receiver":
+                    activity = ComponentParseUtils.parseActivity(separateProcesses,
+                            parsingPackage,
+                            res, parser,
+                            flags, outError,
+                            true, false);
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    hasReceiverOrder |= (activity.order != 0);
+                    parsingPackage.addReceiver(activity);
+                    break;
+                case "service":
+                    ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService(
+                            separateProcesses,
+                            parsingPackage,
+                            res, parser, flags,
+                            outError);
+                    if (s == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    hasServiceOrder |= (s.order != 0);
+                    parsingPackage.addService(s);
+                    break;
+                case "provider":
+                    ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider(
+                            separateProcesses,
+                            parsingPackage,
+                            res, parser, flags,
+                            outError
+                    );
+                    if (p == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.addProvider(p);
+                    break;
+                case "activity-alias":
+                    activity = ComponentParseUtils.parseActivityAlias(
+                            parsingPackage,
+                            res,
+                            parser,
+                            outError
+                    );
+                    if (activity == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    hasActivityOrder |= (activity.order != 0);
+                    parsingPackage.addActivity(activity);
+                    break;
+                case "meta-data":
+                    // note: application meta-data is stored off to the side, so it can
+                    // remain null in the primary copy (we like to avoid extra copies because
+                    // it can be large)
+                    Bundle appMetaData = parseMetaData(parsingPackage, res, parser,
+                            parsingPackage.getAppMetaData(),
+                            outError);
+                    if (appMetaData == null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                outError[0]
+                        );
+                    }
+
+                    parsingPackage.setAppMetaData(appMetaData);
+                    break;
+                case "static-library":
+                    sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestStaticLibrary);
+
+                    // Note: don't allow this value to be a reference to a resource
+                    // that may change.
+                    String lname = sa.getNonResourceString(
+                            R.styleable.AndroidManifestStaticLibrary_name);
+                    final int version = sa.getInt(
+                            R.styleable.AndroidManifestStaticLibrary_version, -1);
+                    final int versionMajor = sa.getInt(
+                            R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                            0);
+
+                    sa.recycle();
+
+                    // Since the app canot run without a static lib - fail if malformed
+                    if (lname == null || version < 0) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Bad static-library declaration name: " + lname
+                                        + " version: " + version
+                        );
+                    }
+
+                    if (parsingPackage.getSharedUserId() != null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+                                "sharedUserId not allowed in static shared library"
+                        );
+                    }
+
+                    if (parsingPackage.getStaticSharedLibName() != null) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Multiple static-shared libs for package " + pkgName
+                        );
+                    }
+
+                    parsingPackage.setStaticSharedLibName(lname.intern());
+                    if (version >= 0) {
+                        parsingPackage.setStaticSharedLibVersion(
+                                PackageInfo.composeLongVersionCode(versionMajor, version));
+                    } else {
+                        parsingPackage.setStaticSharedLibVersion(version);
+                    }
+                    parsingPackage.setStaticSharedLibrary(true);
+
+                    XmlUtils.skipCurrentTag(parser);
+
+                    break;
+                case "library":
+                    sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestLibrary);
+
+                    // Note: don't allow this value to be a reference to a resource
+                    // that may change.
+                    lname = sa.getNonResourceString(
+                            R.styleable.AndroidManifestLibrary_name);
+
+                    sa.recycle();
+
+                    if (lname != null) {
+                        lname = lname.intern();
+                        if (!ArrayUtils.contains(parsingPackage.getLibraryNames(), lname)) {
+                            parsingPackage.addLibraryName(lname);
+                        }
+                    }
+
+                    XmlUtils.skipCurrentTag(parser);
+
+                    break;
+                case "uses-static-library":
+                    ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage,
+                            res, parser);
+                    if (!parseResult.isSuccess()) {
+                        return parseResult;
+                    }
+                    break;
+                case "uses-library":
+                    sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestUsesLibrary);
+
+                    // Note: don't allow this value to be a reference to a resource
+                    // that may change.
+                    lname = sa.getNonResourceString(
+                            R.styleable.AndroidManifestUsesLibrary_name);
+                    boolean req = sa.getBoolean(
+                            R.styleable.AndroidManifestUsesLibrary_required,
+                            true);
+
+                    sa.recycle();
+
+                    if (lname != null) {
+                        lname = lname.intern();
+                        if (req) {
+                            parsingPackage.addUsesLibrary(lname);
+                        } else {
+                            parsingPackage.addUsesOptionalLibrary(lname);
+                        }
+                    }
+
+                    XmlUtils.skipCurrentTag(parser);
+
+                    break;
+                case "uses-package":
+                    // Dependencies for app installers; we don't currently try to
+                    // enforce this.
+                    XmlUtils.skipCurrentTag(parser);
+                    break;
+                case "profileable":
+                    sa = res.obtainAttributes(parser,
+                            R.styleable.AndroidManifestProfileable);
+                    if (sa.getBoolean(
+                            R.styleable.AndroidManifestProfileable_shell, false)) {
+                        parsingPackage.setProfileableByShell(true);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+
+                default:
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG, "Unknown element under <application>: " + tagName
+                                + " at " + parsingPackage.getBaseCodePath() + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Bad element under <application>: " + tagName
+                        );
+                    }
+            }
+        }
+
+        if (TextUtils.isEmpty(parsingPackage.getStaticSharedLibName())) {
+            // Add a hidden app detail activity to normal apps which forwards user to App Details
+            // page.
+            ComponentParseUtils.ParsedActivity a = generateAppDetailsHiddenActivity(
+                    parsingPackage,
+                    outError
+            );
+            // Ignore errors here
+            parsingPackage.addActivity(a);
+        }
+
+        if (hasActivityOrder) {
+            parsingPackage.sortActivities();
+        }
+        if (hasReceiverOrder) {
+            parsingPackage.sortReceivers();
+        }
+        if (hasServiceOrder) {
+            parsingPackage.sortServices();
+        }
+        // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
+        // every activity info has had a chance to set it from its attributes.
+        setMaxAspectRatio(parsingPackage);
+        setMinAspectRatio(parsingPackage, callback);
+
+        parsingPackage.setHasDomainUrls(hasDomainURLs(parsingPackage));
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static ParseResult parseUsesStaticLibrary(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestUsesStaticLibrary);
+
+        // Note: don't allow this value to be a reference to a resource that may change.
+        String lname = sa.getNonResourceString(
+                R.styleable.AndroidManifestUsesLibrary_name);
+        final int version = sa.getInt(
+                R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+        String certSha256Digest = sa.getNonResourceString(com.android.internal.R.styleable
+                .AndroidManifestUsesStaticLibrary_certDigest);
+        sa.recycle();
+
+        // Since an APK providing a static shared lib can only provide the lib - fail if malformed
+        if (lname == null || version < 0 || certSha256Digest == null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Bad uses-static-library declaration name: " + lname + " version: "
+                            + version + " certDigest" + certSha256Digest
+            );
+        }
+
+        // Can depend only on one version of the same library
+        List<String> usesStaticLibraries = parsingPackage.getUsesStaticLibraries();
+        if (usesStaticLibraries != null && usesStaticLibraries.contains(lname)) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Depending on multiple versions of static library " + lname
+            );
+        }
+
+        lname = lname.intern();
+        // We allow ":" delimiters in the SHA declaration as this is the format
+        // emitted by the certtool making it easy for developers to copy/paste.
+        certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+        // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
+        String[] additionalCertSha256Digests = EmptyArray.STRING;
+        if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
+            // TODO(b/135203078): Remove, replace with ParseResult
+            String[] outError = new String[1];
+            additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
+            if (additionalCertSha256Digests == null || outError[0] != null) {
+                return parseInput.error(
+                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                        outError[0]
+                );
+            }
+        } else {
+            XmlUtils.skipCurrentTag(parser);
+        }
+
+        final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+        certSha256Digests[0] = certSha256Digest;
+        System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+                1, additionalCertSha256Digests.length);
+
+        parsingPackage.addUsesStaticLibrary(lname)
+                .addUsesStaticLibraryVersion(version)
+                .addUsesStaticLibraryCertDigests(certSha256Digests);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static String[] parseAdditionalCertificates(
+            Resources resources,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws XmlPullParserException, IOException {
+        String[] certSha256Digests = EmptyArray.STRING;
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            final String nodeName = parser.getName();
+            if (nodeName.equals("additional-certificate")) {
+                final TypedArray sa = resources.obtainAttributes(parser, com.android.internal.
+                        R.styleable.AndroidManifestAdditionalCertificate);
+                String certSha256Digest = sa.getNonResourceString(com.android.internal.
+                        R.styleable.AndroidManifestAdditionalCertificate_certDigest);
+                sa.recycle();
+
+                if (TextUtils.isEmpty(certSha256Digest)) {
+                    outError[0] = "Bad additional-certificate declaration with empty"
+                            + " certDigest:" + certSha256Digest;
+                    XmlUtils.skipCurrentTag(parser);
+                    sa.recycle();
+                    return null;
+                }
+
+                // We allow ":" delimiters in the SHA declaration as this is the format
+                // emitted by the certtool making it easy for developers to copy/paste.
+                certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+                certSha256Digests = ArrayUtils.appendElement(String.class,
+                        certSha256Digests, certSha256Digest);
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+
+        return certSha256Digests;
+    }
+
+    /**
+     * Generate activity object that forwards user to App Details page automatically.
+     * This activity should be invisible to user and user should not know or see it.
+     *
+     * @hide
+     */
+    @NonNull
+    private static ComponentParseUtils.ParsedActivity generateAppDetailsHiddenActivity(
+            ParsingPackage parsingPackage,
+            String[] outError
+    ) {
+        String packageName = parsingPackage.getPackageName();
+        String processName = parsingPackage.getProcessName();
+        boolean hardwareAccelerated = parsingPackage.isBaseHardwareAccelerated();
+        int uiOptions = parsingPackage.getUiOptions();
+
+        // Build custom App Details activity info instead of parsing it from xml
+        ComponentParseUtils.ParsedActivity activity = new ComponentParseUtils.ParsedActivity();
+        activity.setPackageName(packageName);
+
+        activity.theme = android.R.style.Theme_NoDisplay;
+        activity.exported = true;
+        activity.className = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME;
+        activity.setProcessName(processName, processName);
+        activity.uiOptions = uiOptions;
+        activity.taskAffinity = PackageParser.buildTaskAffinityName(packageName,
+                packageName,
+                ":app_details", outError);
+        activity.enabled = true;
+        activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+        activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+        activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+        activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
+        activity.softInputMode = 0;
+        activity.persistableMode = ActivityInfo.PERSIST_NEVER;
+        activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        activity.lockTaskLaunchMode = 0;
+        activity.directBootAware = false;
+        activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+        activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        if (hardwareAccelerated) {
+            activity.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
+        }
+
+        return activity;
+    }
+
+    /**
+     * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+     */
+    private static boolean hasDomainURLs(
+            ParsingPackage parsingPackage) {
+        final List<ComponentParseUtils.ParsedActivity> activities = parsingPackage.getActivities();
+        final int countActivities = activities.size();
+        for (int n = 0; n < countActivities; n++) {
+            ComponentParseUtils.ParsedActivity activity = activities.get(n);
+            List<ComponentParseUtils.ParsedActivityIntentInfo> filters = activity.intents;
+            if (filters == null) continue;
+            final int countFilters = filters.size();
+            for (int m = 0; m < countFilters; m++) {
+                ComponentParseUtils.ParsedActivityIntentInfo aii = filters.get(m);
+                if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
+                if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
+                if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                        aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Sets the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private static void setMaxAspectRatio(
+            ParsingPackage parsingPackage) {
+        // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+        // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+        float maxAspectRatio = parsingPackage.getTargetSdkVersion() < O
+                ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+
+        float packageMaxAspectRatio = parsingPackage.getMaxAspectRatio();
+        if (packageMaxAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            maxAspectRatio = packageMaxAspectRatio;
+        } else {
+            Bundle appMetaData = parsingPackage.getAppMetaData();
+            if (appMetaData != null && appMetaData.containsKey(
+                    PackageParser.METADATA_MAX_ASPECT_RATIO)) {
+                maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+                        maxAspectRatio);
+            }
+        }
+
+        if (parsingPackage.getActivities() != null) {
+            for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) {
+                // If the max aspect ratio for the activity has already been set, skip.
+                if (activity.hasMaxAspectRatio()) {
+                    continue;
+                }
+
+                // By default we prefer to use a values defined on the activity directly than values
+                // defined on the application. We do not check the styled attributes on the activity
+                // as it would have already been set when we processed the activity. We wait to
+                // process the meta data here since this method is called at the end of processing
+                // the application and all meta data is guaranteed.
+                final float activityAspectRatio = activity.metaData != null
+                        ? activity.metaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+                        maxAspectRatio)
+                        : maxAspectRatio;
+
+                activity.setMaxAspectRatio(activity.resizeMode, activityAspectRatio);
+            }
+        }
+    }
+
+    /**
+     * Sets the min aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private static void setMinAspectRatio(
+            ParsingPackage parsingPackage,
+            PackageParser.Callback callback
+    ) {
+        final float minAspectRatio;
+        float packageMinAspectRatio = parsingPackage.getMinAspectRatio();
+        if (packageMinAspectRatio != 0) {
+            // Use the application max aspect ration as default if set.
+            minAspectRatio = packageMinAspectRatio;
+        } else {
+            // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+            // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+            // except for watches which always supported 1:1.
+            minAspectRatio = parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+                    ? 0
+                    : (callback != null && callback.hasFeature(FEATURE_WATCH))
+                            ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+                            : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+        }
+
+        if (parsingPackage.getActivities() != null) {
+            for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) {
+                if (activity.hasMinAspectRatio()) {
+                    continue;
+                }
+                activity.setMinAspectRatio(activity.resizeMode, minAspectRatio);
+            }
+        }
+    }
+
+    private static ParseResult parseOverlay(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
+        String target = sa.getString(
+                R.styleable.AndroidManifestResourceOverlay_targetPackage);
+        String targetName = sa.getString(
+                R.styleable.AndroidManifestResourceOverlay_targetName);
+        String category = sa.getString(
+                R.styleable.AndroidManifestResourceOverlay_category);
+        int priority = sa.getInt(R.styleable.AndroidManifestResourceOverlay_priority,
+                0);
+        boolean isStatic = sa.getBoolean(
+                R.styleable.AndroidManifestResourceOverlay_isStatic, false);
+
+        if (target == null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<overlay> does not specify a target package"
+            );
+        }
+
+        if (priority < 0 || priority > 9999) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "<overlay> priority must be between 0 and 9999"
+            );
+        }
+
+        // check to see if overlay should be excluded based on system property condition
+        String propName = sa.getString(
+                R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
+        String propValue = sa.getString(
+                R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
+        if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
+            Slog.i(TAG, "Skipping target and overlay pair " + target + " and "
+                    + parsingPackage.getBaseCodePath()
+                    + ": overlay ignored due to required system property: "
+                    + propName + " with value: " + propValue);
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    "Skipping target and overlay pair " + target + " and "
+                            + parsingPackage.getBaseCodePath()
+                            + ": overlay ignored due to required system property: "
+                            + propName + " with value: " + propValue
+            );
+        }
+
+        parsingPackage
+                .setIsOverlay(true)
+                .setOverlayTarget(target)
+                .setOverlayTargetName(targetName)
+                .setOverlayCategory(category)
+                .setOverlayPriority(priority)
+                .setOverlayIsStatic(isStatic);
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+        return parseInput.success(parsingPackage);
+    }
+
+    private static boolean parseProtectedBroadcast(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestProtectedBroadcast);
+
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        String name = sa.getNonResourceString(R.styleable.AndroidManifestProtectedBroadcast_name);
+
+        sa.recycle();
+
+        if (name != null) {
+            parsingPackage.addProtectedBroadcast(name);
+        }
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static boolean parseSupportScreens(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestSupportsScreens);
+
+        int requiresSmallestWidthDp = sa.getInteger(
+                R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
+                0);
+        int compatibleWidthLimitDp = sa.getInteger(
+                R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
+                0);
+        int largestWidthLimitDp = sa.getInteger(
+                R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
+                0);
+
+        // This is a trick to get a boolean and still able to detect
+        // if a value was actually set.
+        parsingPackage
+                .setSupportsSmallScreens(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_smallScreens, 1))
+                .setSupportsNormalScreens(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_normalScreens, 1))
+                .setSupportsLargeScreens(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_largeScreens, 1))
+                .setSupportsXLargeScreens(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_xlargeScreens, 1))
+                .setResizeable(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_resizeable, 1))
+                .setAnyDensity(
+                        sa.getInteger(R.styleable.AndroidManifestSupportsScreens_anyDensity, 1))
+                .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
+                .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
+                .setLargestWidthLimitDp(largestWidthLimitDp);
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static ParseResult parseInstrumentation(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws XmlPullParserException, IOException {
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+
+        ComponentParseUtils.ParsedInstrumentation parsedInstrumentation =
+                ComponentParseUtils.parseInstrumentation(parsingPackage,
+                        res, parser, outError);
+
+        if (parsedInstrumentation == null || outError[0] != null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    outError[0]
+            );
+        }
+
+        parsingPackage.addInstrumentation(parsedInstrumentation);
+
+        return parseInput.success(parsingPackage);
+    }
+
+    private static boolean parseOriginalPackage(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestOriginalPackage);
+
+        String orig = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestOriginalPackage_name,
+                0);
+        if (!parsingPackage.getPackageName().equals(orig)) {
+            if (parsingPackage.getOriginalPackages() == null) {
+                parsingPackage.setRealPackage(parsingPackage.getPackageName());
+            }
+            parsingPackage.addOriginalPackage(orig);
+        }
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static boolean parseAdoptPermissions(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestOriginalPackage);
+
+        String name = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestOriginalPackage_name,
+                0);
+
+        sa.recycle();
+
+        if (name != null) {
+            parsingPackage.addAdoptPermission(name);
+        }
+
+        XmlUtils.skipCurrentTag(parser);
+        return true;
+    }
+
+    private static void convertNewPermissions(
+            ParsingPackage packageToParse) {
+        final int NP = PackageParser.NEW_PERMISSIONS.length;
+        StringBuilder newPermsMsg = null;
+        for (int ip = 0; ip < NP; ip++) {
+            final PackageParser.NewPermissionInfo npi
+                    = PackageParser.NEW_PERMISSIONS[ip];
+            if (packageToParse.getTargetSdkVersion() >= npi.sdkVersion) {
+                break;
+            }
+            if (!packageToParse.getRequestedPermissions().contains(npi.name)) {
+                if (newPermsMsg == null) {
+                    newPermsMsg = new StringBuilder(128);
+                    newPermsMsg.append(packageToParse.getPackageName());
+                    newPermsMsg.append(": compat added ");
+                } else {
+                    newPermsMsg.append(' ');
+                }
+                newPermsMsg.append(npi.name);
+                packageToParse.addRequestedPermission(npi.name);
+                packageToParse.addImplicitPermission(npi.name);
+            }
+        }
+        if (newPermsMsg != null) {
+            Slog.i(TAG, newPermsMsg.toString());
+        }
+    }
+
+    private static void convertSplitPermissions(ParsingPackage packageToParse) {
+        List<SplitPermissionInfoParcelable> splitPermissions;
+
+        try {
+            splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        final int listSize = splitPermissions.size();
+        for (int is = 0; is < listSize; is++) {
+            final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
+            List<String> requestedPermissions = packageToParse.getRequestedPermissions();
+            if (packageToParse.getTargetSdkVersion() >= spi.getTargetSdk()
+                    || !requestedPermissions.contains(spi.getSplitPermission())) {
+                continue;
+            }
+            final List<String> newPerms = spi.getNewPermissions();
+            for (int in = 0; in < newPerms.size(); in++) {
+                final String perm = newPerms.get(in);
+                if (!requestedPermissions.contains(perm)) {
+                    packageToParse.addRequestedPermission(perm);
+                    packageToParse.addImplicitPermission(perm);
+                }
+            }
+        }
+    }
+
+    private static boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
+        if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
+            if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
+                // malformed condition - incomplete
+                Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
+                        + "=" + propValue + "' - require both requiredSystemPropertyName"
+                        + " AND requiredSystemPropertyValue to be specified.");
+                return false;
+            }
+            // no valid condition set - so no exclusion criteria, overlay will be included.
+            return true;
+        }
+
+        // check property value - make sure it is both set and equal to expected value
+        final String currValue = SystemProperties.get(propName);
+        return (currValue != null && currValue.equals(propValue));
+    }
+
+    /**
+     * This is a pre-density application which will get scaled - instead of being pixel perfect.
+     * This type of application is not resizable.
+     *
+     * @param parsingPackage The package which needs to be marked as unresizable.
+     */
+    private static void adjustPackageToBeUnresizeableAndUnpipable(
+            ParsingPackage parsingPackage) {
+        if (parsingPackage.getActivities() != null) {
+            for (ComponentParseUtils.ParsedActivity a : parsingPackage.getActivities()) {
+                a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+                a.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+            }
+        }
+    }
+
+    private static String validateName(String name, boolean requireSeparator,
+            boolean requireFilename) {
+        final int N = name.length();
+        boolean hasSep = false;
+        boolean front = true;
+        for (int i = 0; i < N; i++) {
+            final char c = name.charAt(i);
+            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                front = false;
+                continue;
+            }
+            if (!front) {
+                if ((c >= '0' && c <= '9') || c == '_') {
+                    continue;
+                }
+            }
+            if (c == '.') {
+                hasSep = true;
+                front = true;
+                continue;
+            }
+            return "bad character '" + c + "'";
+        }
+        if (requireFilename && !FileUtils.isValidExtFilename(name)) {
+            return "Invalid filename";
+        }
+        return hasSep || !requireSeparator
+                ? null : "must have at least one '.' separator";
+    }
+
+    public static Bundle parseMetaData(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser, Bundle data, String[] outError)
+            throws XmlPullParserException, IOException {
+
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestMetaData);
+
+        if (data == null) {
+            data = new Bundle();
+        }
+
+        String name = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestMetaData_name, 0);
+        if (name == null) {
+            outError[0] = "<meta-data> requires an android:name attribute";
+            sa.recycle();
+            return null;
+        }
+
+        name = name.intern();
+
+        TypedValue v = sa.peekValue(
+                R.styleable.AndroidManifestMetaData_resource);
+        if (v != null && v.resourceId != 0) {
+            //Slog.i(TAG, "Meta data ref " + name + ": " + v);
+            data.putInt(name, v.resourceId);
+        } else {
+            v = sa.peekValue(
+                    R.styleable.AndroidManifestMetaData_value);
+            //Slog.i(TAG, "Meta data " + name + ": " + v);
+            if (v != null) {
+                if (v.type == TypedValue.TYPE_STRING) {
+                    CharSequence cs = v.coerceToString();
+                    data.putString(name, cs != null ? cs.toString() : null);
+                } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                    data.putBoolean(name, v.data != 0);
+                } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                        && v.type <= TypedValue.TYPE_LAST_INT) {
+                    data.putInt(name, v.data);
+                } else if (v.type == TypedValue.TYPE_FLOAT) {
+                    data.putFloat(name, v.getFloat());
+                } else {
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG,
+                                "<meta-data> only supports string, integer, float, color, "
+                                        + "boolean, and resource reference types: "
+                                        + parser.getName() + " at "
+                                        + parsingPackage.getBaseCodePath() + " "
+                                        + parser.getPositionDescription());
+                    } else {
+                        outError[0] =
+                                "<meta-data> only supports string, integer, float, color, "
+                                        + "boolean, and resource reference types";
+                        data = null;
+                    }
+                }
+            } else {
+                outError[0] = "<meta-data> requires an android:value or android:resource attribute";
+                data = null;
+            }
+        }
+
+        sa.recycle();
+
+        XmlUtils.skipCurrentTag(parser);
+
+        return data;
+    }
+
+    /**
+     * Collect certificates from all the APKs described in the given package,
+     * populating {@link AndroidPackageWrite#setSigningDetails(SigningDetails)}. Also asserts that
+     * all APK contents are signed correctly and consistently.
+     */
+    public static void collectCertificates(AndroidPackage pkg, boolean skipVerify)
+            throws PackageParserException {
+        pkg.mutate().setSigningDetails(SigningDetails.UNKNOWN);
+
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+        try {
+            pkg.mutate().setSigningDetails(collectCertificates(
+                    pkg.getBaseCodePath(),
+                    skipVerify,
+                    pkg.isStaticSharedLibrary(),
+                    pkg.getSigningDetails()
+            ));
+
+            String[] splitCodePaths = pkg.getSplitCodePaths();
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                for (int i = 0; i < splitCodePaths.length; i++) {
+                    pkg.mutate().setSigningDetails(collectCertificates(
+                            splitCodePaths[i],
+                            skipVerify,
+                            pkg.isStaticSharedLibrary(),
+                            pkg.getSigningDetails()
+                    ));
+                }
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    public static SigningDetails collectCertificates(
+            String baseCodePath,
+            boolean skipVerify,
+            boolean isStaticSharedLibrary,
+            @NonNull SigningDetails existingSigningDetails
+    ) throws PackageParserException {
+        int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR;
+        if (isStaticSharedLibrary) {
+            // must use v2 signing scheme
+            minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+        }
+        SigningDetails verified;
+        if (skipVerify) {
+            // systemDir APKs are already trusted, save time by not verifying
+            verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+                    baseCodePath, minSignatureScheme);
+        } else {
+            verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
+        }
+
+        // Verify that entries are signed consistently with the first pkg
+        // we encountered. Note that for splits, certificates may have
+        // already been populated during an earlier parse of a base APK.
+        if (existingSigningDetails == SigningDetails.UNKNOWN) {
+            return verified;
+        } else {
+            if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+                throw new PackageParserException(
+                        INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                        baseCodePath + " has mismatched certificates");
+            }
+
+            return existingSigningDetails;
+        }
+    }
+
+    @Nullable
+    public static String buildClassName(String pkg, CharSequence clsSeq) {
+        if (clsSeq == null || clsSeq.length() <= 0) {
+            return null;
+        }
+        String cls = clsSeq.toString();
+        char c = cls.charAt(0);
+        if (c == '.') {
+            return pkg + cls;
+        }
+        if (cls.indexOf('.') < 0) {
+            StringBuilder b = new StringBuilder(pkg);
+            b.append('.');
+            b.append(cls);
+            return b.toString();
+        }
+        return cls;
+    }
+
+    public interface ParseInput {
+        ParseResult success(ParsingPackage result);
+
+        ParseResult error(int parseError);
+
+        ParseResult error(int parseError, String errorMessage);
+    }
+
+    public static class ParseResult implements ParseInput {
+
+        private static final boolean DEBUG_FILL_STACK_TRACE = false;
+
+        private ParsingPackage result;
+
+        private int parseError;
+        private String errorMessage;
+
+        public ParseInput reset() {
+            this.result = null;
+            this.parseError = PackageManager.INSTALL_SUCCEEDED;
+            this.errorMessage = null;
+            return this;
+        }
+
+        @Override
+        public ParseResult success(ParsingPackage result) {
+            if (parseError != PackageManager.INSTALL_SUCCEEDED || errorMessage != null) {
+                throw new IllegalStateException("Cannot set to success after set to error");
+            }
+            this.result = result;
+            return this;
+        }
+
+        @Override
+        public ParseResult error(int parseError) {
+            return error(parseError, null);
+        }
+
+        @Override
+        public ParseResult error(int parseError, String errorMessage) {
+            this.parseError = parseError;
+            this.errorMessage = errorMessage;
+
+            if (DEBUG_FILL_STACK_TRACE) {
+                this.errorMessage += Arrays.toString(new Exception().getStackTrace());
+            }
+
+            return this;
+        }
+
+        public ParsingPackage getResultAndNull() {
+            ParsingPackage result = this.result;
+            this.result = null;
+            return result;
+        }
+
+        public boolean isSuccess() {
+            return parseError == PackageManager.INSTALL_SUCCEEDED;
+        }
+
+        public int getParseError() {
+            return parseError;
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
new file mode 100644
index 0000000..adf2a4f
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -0,0 +1,3289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.CallSuper;
+import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.Gravity;
+
+import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * TODO(b/135203078): Move the inner classes out to separate files.
+ * TODO(b/135203078): Expose inner classes as immutable through interface methods.
+ *
+ * @hide
+ */
+public class ComponentParseUtils {
+
+    private static final String TAG = ApkParseUtils.TAG;
+
+    // TODO(b/135203078): None of this class's subclasses do anything. Remove in favor of base?
+    public static class ParsedIntentInfo extends IntentFilter {
+
+        /**
+         * <p>
+         * Implementation note: The serialized form for the intent list also contains the name
+         * of the concrete class that's stored in the list, and assumes that every element of the
+         * list is of the same type. This is very similar to the original parcelable mechanism.
+         * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
+         * and is public API. It also declares Parcelable related methods as final which means
+         * we can't extend them. The approach of using composition instead of inheritance leads to
+         * a large set of cascading changes in the PackageManagerService, which seem undesirable.
+         *
+         * <p>
+         * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
+         * to make sure their owner fields are consistent. See {@code fixupOwner}.
+         */
+        public static void writeIntentsList(List<? extends ParsedIntentInfo> list, Parcel out,
+                int flags) {
+            if (list == null) {
+                out.writeInt(-1);
+                return;
+            }
+
+            final int size = list.size();
+            out.writeInt(size);
+
+            // Don't bother writing the component name if the list is empty.
+            if (size > 0) {
+                ParsedIntentInfo info = list.get(0);
+                out.writeString(info.getClass().getName());
+
+                for (int i = 0; i < size; i++) {
+                    list.get(i).writeIntentInfoToParcel(out, flags);
+                }
+            }
+        }
+
+        public static <T extends ParsedIntentInfo> ArrayList<T> createIntentsList(Parcel in) {
+            int size = in.readInt();
+            if (size == -1) {
+                return null;
+            }
+
+            if (size == 0) {
+                return new ArrayList<>(0);
+            }
+
+            String className = in.readString();
+            final ArrayList<T> intentsList;
+            try {
+                final Class<T> cls = (Class<T>) Class.forName(className);
+                final Constructor<T> cons = cls.getConstructor(Parcel.class);
+
+                intentsList = new ArrayList<>(size);
+                for (int i = 0; i < size; ++i) {
+                    intentsList.add(cons.newInstance(in));
+                }
+            } catch (ReflectiveOperationException ree) {
+                throw new AssertionError("Unable to construct intent list for: "
+                        + className, ree);
+            }
+
+            return intentsList;
+        }
+
+        protected String packageName;
+        protected final String className;
+
+        public boolean hasDefault;
+        public int labelRes;
+        public CharSequence nonLocalizedLabel;
+        public int icon;
+
+        protected List<String> rawDataTypes;
+
+        public void addRawDataType(String dataType) throws MalformedMimeTypeException {
+            if (rawDataTypes == null) {
+                rawDataTypes = new ArrayList<>();
+            }
+
+            rawDataTypes.add(dataType);
+            addDataType(dataType);
+        }
+
+        public ParsedIntentInfo(String packageName, String className) {
+            this.packageName = packageName;
+            this.className = className;
+        }
+
+        public ParsedIntentInfo(Parcel in) {
+            super(in);
+            packageName = in.readString();
+            className = in.readString();
+            hasDefault = (in.readInt() == 1);
+            labelRes = in.readInt();
+            nonLocalizedLabel = in.readCharSequence();
+            icon = in.readInt();
+        }
+
+        public void writeIntentInfoToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeString(packageName);
+            dest.writeString(className);
+            dest.writeInt(hasDefault ? 1 : 0);
+            dest.writeInt(labelRes);
+            dest.writeCharSequence(nonLocalizedLabel);
+            dest.writeInt(icon);
+        }
+
+        public String getPackageName() {
+            return packageName;
+        }
+
+        public String getClassName() {
+            return className;
+        }
+    }
+
+    public static class ParsedActivityIntentInfo extends ParsedIntentInfo {
+
+        public ParsedActivityIntentInfo(String packageName, String className) {
+            super(packageName, className);
+        }
+
+        public ParsedActivityIntentInfo(Parcel in) {
+            super(in);
+        }
+
+        public static final Creator<ParsedActivityIntentInfo> CREATOR =
+                new Creator<ParsedActivityIntentInfo>() {
+                    @Override
+                    public ParsedActivityIntentInfo createFromParcel(Parcel source) {
+                        return new ParsedActivityIntentInfo(source);
+                    }
+
+                    @Override
+                    public ParsedActivityIntentInfo[] newArray(int size) {
+                        return new ParsedActivityIntentInfo[size];
+                    }
+                };
+    }
+
+    public static class ParsedServiceIntentInfo extends ParsedIntentInfo {
+
+        public ParsedServiceIntentInfo(String packageName, String className) {
+            super(packageName, className);
+        }
+
+        public ParsedServiceIntentInfo(Parcel in) {
+            super(in);
+        }
+
+        public static final Creator<ParsedServiceIntentInfo> CREATOR =
+                new Creator<ParsedServiceIntentInfo>() {
+                    @Override
+                    public ParsedServiceIntentInfo createFromParcel(Parcel source) {
+                        return new ParsedServiceIntentInfo(source);
+                    }
+
+                    @Override
+                    public ParsedServiceIntentInfo[] newArray(int size) {
+                        return new ParsedServiceIntentInfo[size];
+                    }
+                };
+    }
+
+    public static class ParsedProviderIntentInfo extends ParsedIntentInfo {
+
+        public ParsedProviderIntentInfo(String packageName, String className) {
+            super(packageName, className);
+        }
+
+        public ParsedProviderIntentInfo(Parcel in) {
+            super(in);
+        }
+
+        public static final Creator<ParsedProviderIntentInfo> CREATOR =
+                new Creator<ParsedProviderIntentInfo>() {
+                    @Override
+                    public ParsedProviderIntentInfo createFromParcel(Parcel source) {
+                        return new ParsedProviderIntentInfo(source);
+                    }
+
+                    @Override
+                    public ParsedProviderIntentInfo[] newArray(int size) {
+                        return new ParsedProviderIntentInfo[size];
+                    }
+                };
+    }
+
+    public static class ParsedQueriesIntentInfo extends ParsedIntentInfo {
+
+        public ParsedQueriesIntentInfo(String packageName, String className) {
+            super(packageName, className);
+        }
+
+        public ParsedQueriesIntentInfo(Parcel in) {
+            super(in);
+        }
+
+        public static final Creator<ParsedQueriesIntentInfo> CREATOR =
+                new Creator<ParsedQueriesIntentInfo>() {
+                    @Override
+                    public ParsedQueriesIntentInfo createFromParcel(Parcel source) {
+                        return new ParsedQueriesIntentInfo(source);
+                    }
+
+                    @Override
+                    public ParsedQueriesIntentInfo[] newArray(int size) {
+                        return new ParsedQueriesIntentInfo[size];
+                    }
+                };
+    }
+
+    public static class ParsedComponent<IntentInfoType extends ParsedIntentInfo> implements
+            Parcelable {
+
+        // TODO(b/135203078): Replace with "name", as not all usages are an actual class
+        public String className;
+        public int icon;
+        public int labelRes;
+        public CharSequence nonLocalizedLabel;
+        public int logo;
+        public int banner;
+
+        public int descriptionRes;
+
+        // TODO(b/135203078): Make subclass that contains these fields only for the necessary
+        //  subtypes
+        protected boolean enabled = true;
+        protected boolean directBootAware;
+        public int flags;
+
+        private String packageName;
+        private String splitName;
+
+        // TODO(b/135203078): Make nullable
+        public List<IntentInfoType> intents = new ArrayList<>();
+
+        private transient ComponentName componentName;
+
+        protected Bundle metaData;
+
+        public void setSplitName(String splitName) {
+            this.splitName = splitName;
+        }
+
+        public String getSplitName() {
+            return splitName;
+        }
+
+        @CallSuper
+        public void setPackageName(String packageName) {
+            this.packageName = packageName;
+            this.componentName = null;
+        }
+
+        void setPackageNameInternal(String packageName) {
+            this.packageName = packageName;
+            this.componentName = null;
+        }
+
+        public String getPackageName() {
+            return packageName;
+        }
+
+        public final boolean isDirectBootAware() {
+            return directBootAware;
+        }
+
+        public final boolean isEnabled() {
+            return enabled;
+        }
+
+        public final String getName() {
+            return className;
+        }
+
+        public final Bundle getMetaData() {
+            return metaData;
+        }
+
+        @UnsupportedAppUsage
+        public ComponentName getComponentName() {
+            if (componentName != null) {
+                return componentName;
+            }
+            if (className != null) {
+                componentName = new ComponentName(getPackageName(),
+                        className);
+            }
+            return componentName;
+        }
+
+        public void setFrom(ParsedComponent other) {
+            this.metaData = other.metaData;
+            this.className = other.className;
+            this.icon = other.icon;
+            this.labelRes = other.labelRes;
+            this.nonLocalizedLabel = other.nonLocalizedLabel;
+            this.logo = other.logo;
+            this.banner = other.banner;
+
+            this.descriptionRes = other.descriptionRes;
+
+            this.enabled = other.enabled;
+            this.directBootAware = other.directBootAware;
+            this.flags = other.flags;
+
+            this.setPackageName(other.packageName);
+            this.setSplitName(other.getSplitName());
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(this.className);
+            dest.writeInt(this.icon);
+            dest.writeInt(this.labelRes);
+            dest.writeCharSequence(this.nonLocalizedLabel);
+            dest.writeInt(this.logo);
+            dest.writeInt(this.banner);
+            dest.writeInt(this.descriptionRes);
+            dest.writeBoolean(this.enabled);
+            dest.writeBoolean(this.directBootAware);
+            dest.writeInt(this.flags);
+            dest.writeString(this.packageName);
+            dest.writeString(this.splitName);
+            ParsedIntentInfo.writeIntentsList(this.intents, dest, flags);
+            dest.writeBundle(this.metaData);
+        }
+
+        public ParsedComponent() {
+        }
+
+        protected ParsedComponent(Parcel in) {
+            // We use the boot classloader for all classes that we load.
+            final ClassLoader boot = Object.class.getClassLoader();
+            this.className = in.readString();
+            this.icon = in.readInt();
+            this.labelRes = in.readInt();
+            this.nonLocalizedLabel = in.readCharSequence();
+            this.logo = in.readInt();
+            this.banner = in.readInt();
+            this.descriptionRes = in.readInt();
+            this.enabled = in.readByte() != 0;
+            this.directBootAware = in.readByte() != 0;
+            this.flags = in.readInt();
+            this.packageName = in.readString();
+            this.splitName = in.readString();
+            this.intents = ParsedIntentInfo.createIntentsList(in);
+            this.metaData = in.readBundle(boot);
+        }
+    }
+
+    // TODO(b/135203078): Document this. Maybe split out ParsedComponent to be actual components
+    //  that can have their own processes, rather than something like permission which cannot.
+    public static class ParsedMainComponent<IntentInfoType extends ParsedIntentInfo> extends
+            ParsedComponent<IntentInfoType> {
+
+        private String processName;
+        private String permission;
+
+        public void setProcessName(String appProcessName, String processName) {
+            // TODO(b/135203078): Is this even necessary anymore?
+            this.processName = TextUtils.safeIntern(
+                    processName == null ? appProcessName : processName);
+        }
+
+        public String getProcessName() {
+            return processName;
+        }
+
+        public void setPermission(String permission) {
+            this.permission = TextUtils.safeIntern(permission);
+        }
+
+        public String getPermission() {
+            return permission;
+        }
+
+        @Override
+        public void setFrom(ParsedComponent other) {
+            super.setFrom(other);
+            if (other instanceof ParsedMainComponent) {
+                ParsedMainComponent otherMainComponent = (ParsedMainComponent) other;
+                this.setProcessName(otherMainComponent.getProcessName(),
+                        otherMainComponent.getProcessName());
+                this.setPermission(otherMainComponent.getPermission());
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeString(this.processName);
+            dest.writeString(this.permission);
+        }
+
+        public ParsedMainComponent() {
+        }
+
+        protected ParsedMainComponent(Parcel in) {
+            super(in);
+            this.processName = TextUtils.safeIntern(in.readString());
+            this.permission = TextUtils.safeIntern(in.readString());
+        }
+
+        public static final Creator<ParsedMainComponent> CREATOR =
+                new Creator<ParsedMainComponent>() {
+                    @Override
+                    public ParsedMainComponent createFromParcel(Parcel source) {
+                        return new ParsedMainComponent(source);
+                    }
+
+                    @Override
+                    public ParsedMainComponent[] newArray(int size) {
+                        return new ParsedMainComponent[size];
+                    }
+                };
+    }
+
+    public static class ParsedActivity extends ParsedMainComponent<ParsedActivityIntentInfo>
+            implements Parcelable {
+
+        public boolean exported;
+        public int theme;
+        public int uiOptions;
+
+        public String targetActivity;
+
+        public String parentActivityName;
+        public String taskAffinity;
+        public int privateFlags;
+
+        public int launchMode;
+        public int documentLaunchMode;
+        public int maxRecents;
+        public int configChanges;
+        public int softInputMode;
+        public int persistableMode;
+        public int lockTaskLaunchMode;
+
+        public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        public int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+        public float maxAspectRatio;
+        public boolean hasMaxAspectRatio;
+
+        public float minAspectRatio;
+        public boolean hasMinAspectRatio;
+
+        public String requestedVrComponent;
+        public int rotationAnimation = -1;
+        public int colorMode;
+        public int order;
+
+        public ActivityInfo.WindowLayout windowLayout;
+
+        @Override
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            for (ParsedIntentInfo intent : this.intents) {
+                intent.packageName = packageName;
+            }
+        }
+
+        public boolean hasMaxAspectRatio() {
+            return hasMaxAspectRatio;
+        }
+
+        public boolean hasMinAspectRatio() {
+            return hasMinAspectRatio;
+        }
+
+        public void setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
+            if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
+                    || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            this.maxAspectRatio = maxAspectRatio;
+            hasMaxAspectRatio = true;
+        }
+
+        public void setMinAspectRatio(int resizeMode, float minAspectRatio) {
+            if (resizeMode == RESIZE_MODE_RESIZEABLE
+                    || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            this.minAspectRatio = minAspectRatio;
+            hasMinAspectRatio = true;
+        }
+
+        public void addIntent(ParsedActivityIntentInfo intent) {
+            this.intents.add(intent);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeBoolean(this.exported);
+            dest.writeInt(this.theme);
+            dest.writeInt(this.uiOptions);
+            dest.writeString(this.targetActivity);
+            dest.writeString(this.parentActivityName);
+            dest.writeString(this.taskAffinity);
+            dest.writeInt(this.privateFlags);
+            dest.writeInt(this.launchMode);
+            dest.writeInt(this.documentLaunchMode);
+            dest.writeInt(this.maxRecents);
+            dest.writeInt(this.configChanges);
+            dest.writeInt(this.softInputMode);
+            dest.writeInt(this.persistableMode);
+            dest.writeInt(this.lockTaskLaunchMode);
+            dest.writeInt(this.screenOrientation);
+            dest.writeInt(this.resizeMode);
+            dest.writeFloat(this.maxAspectRatio);
+            dest.writeBoolean(this.hasMaxAspectRatio);
+            dest.writeFloat(this.minAspectRatio);
+            dest.writeBoolean(this.hasMinAspectRatio);
+            dest.writeString(this.requestedVrComponent);
+            dest.writeInt(this.rotationAnimation);
+            dest.writeInt(this.colorMode);
+            dest.writeInt(this.order);
+            dest.writeBundle(this.metaData);
+
+            if (windowLayout != null) {
+                dest.writeInt(1);
+                dest.writeInt(windowLayout.width);
+                dest.writeFloat(windowLayout.widthFraction);
+                dest.writeInt(windowLayout.height);
+                dest.writeFloat(windowLayout.heightFraction);
+                dest.writeInt(windowLayout.gravity);
+                dest.writeInt(windowLayout.minWidth);
+                dest.writeInt(windowLayout.minHeight);
+            } else {
+                dest.writeInt(0);
+            }
+        }
+
+        public ParsedActivity() {
+        }
+
+        protected ParsedActivity(Parcel in) {
+            super(in);
+            this.exported = in.readByte() != 0;
+            this.theme = in.readInt();
+            this.uiOptions = in.readInt();
+            this.targetActivity = in.readString();
+            this.parentActivityName = in.readString();
+            this.taskAffinity = in.readString();
+            this.privateFlags = in.readInt();
+            this.launchMode = in.readInt();
+            this.documentLaunchMode = in.readInt();
+            this.maxRecents = in.readInt();
+            this.configChanges = in.readInt();
+            this.softInputMode = in.readInt();
+            this.persistableMode = in.readInt();
+            this.lockTaskLaunchMode = in.readInt();
+            this.screenOrientation = in.readInt();
+            this.resizeMode = in.readInt();
+            this.maxAspectRatio = in.readFloat();
+            this.hasMaxAspectRatio = in.readByte() != 0;
+            this.minAspectRatio = in.readFloat();
+            this.hasMinAspectRatio = in.readByte() != 0;
+            this.requestedVrComponent = in.readString();
+            this.rotationAnimation = in.readInt();
+            this.colorMode = in.readInt();
+            this.order = in.readInt();
+            this.metaData = in.readBundle();
+            if (in.readInt() == 1) {
+                windowLayout = new ActivityInfo.WindowLayout(in);
+            }
+        }
+
+        public static final Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
+            @Override
+            public ParsedActivity createFromParcel(Parcel source) {
+                return new ParsedActivity(source);
+            }
+
+            @Override
+            public ParsedActivity[] newArray(int size) {
+                return new ParsedActivity[size];
+            }
+        };
+    }
+
+    public static class ParsedService extends ParsedMainComponent<ParsedServiceIntentInfo> {
+
+        public boolean exported;
+        public int flags;
+        public int foregroundServiceType;
+        public int order;
+
+        @Override
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            for (ParsedIntentInfo intent : this.intents) {
+                intent.packageName = packageName;
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeBoolean(this.exported);
+            dest.writeBundle(this.metaData);
+            dest.writeInt(this.flags);
+            dest.writeInt(this.foregroundServiceType);
+            dest.writeInt(this.order);
+        }
+
+        public ParsedService() {
+        }
+
+        protected ParsedService(Parcel in) {
+            super(in);
+            this.exported = in.readByte() != 0;
+            this.metaData = in.readBundle();
+            this.flags = in.readInt();
+            this.foregroundServiceType = in.readInt();
+            this.order = in.readInt();
+        }
+
+        public static final Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
+            @Override
+            public ParsedService createFromParcel(Parcel source) {
+                return new ParsedService(source);
+            }
+
+            @Override
+            public ParsedService[] newArray(int size) {
+                return new ParsedService[size];
+            }
+        };
+    }
+
+    public static class ParsedProvider extends ParsedMainComponent<ParsedProviderIntentInfo> {
+
+        protected boolean exported;
+        protected int flags;
+        protected int order;
+        private String authority;
+        protected boolean isSyncable;
+        private String readPermission;
+        private String writePermission;
+        protected boolean grantUriPermissions;
+        protected boolean forceUriPermissions;
+        protected boolean multiProcess;
+        protected int initOrder;
+        protected PatternMatcher[] uriPermissionPatterns;
+        protected PathPermission[] pathPermissions;
+
+        protected void setFrom(ParsedProvider other) {
+            super.setFrom(other);
+            this.exported = other.exported;
+
+            this.intents.clear();
+            if (other.intents != null) {
+                this.intents.addAll(other.intents);
+            }
+
+            this.flags = other.flags;
+            this.order = other.order;
+            this.setAuthority(other.getAuthority());
+            this.isSyncable = other.isSyncable;
+            this.setReadPermission(other.getReadPermission());
+            this.setWritePermission(other.getWritePermission());
+            this.grantUriPermissions = other.grantUriPermissions;
+            this.forceUriPermissions = other.forceUriPermissions;
+            this.multiProcess = other.multiProcess;
+            this.initOrder = other.initOrder;
+            this.uriPermissionPatterns = other.uriPermissionPatterns;
+            this.pathPermissions = other.pathPermissions;
+        }
+
+        @Override
+        public void setPackageName(String packageName) {
+            super.setPackageName(packageName);
+            for (ParsedIntentInfo intent : this.intents) {
+                intent.packageName = packageName;
+            }
+        }
+
+        public boolean isExported() {
+            return exported;
+        }
+
+        public List<ParsedProviderIntentInfo> getIntents() {
+            return intents;
+        }
+
+        public int getFlags() {
+            return flags;
+        }
+
+        public int getOrder() {
+            return order;
+        }
+
+        public void setAuthority(String authority) {
+            this.authority = TextUtils.safeIntern(authority);
+        }
+
+        public String getAuthority() {
+            return authority;
+        }
+
+        public boolean isSyncable() {
+            return isSyncable;
+        }
+
+        public void setReadPermission(String readPermission) {
+            this.readPermission = TextUtils.safeIntern(readPermission);
+        }
+
+        public String getReadPermission() {
+            return readPermission;
+        }
+
+        public void setWritePermission(String writePermission) {
+            this.writePermission = TextUtils.safeIntern(writePermission);
+        }
+
+        public String getWritePermission() {
+            return writePermission;
+        }
+
+        public boolean isGrantUriPermissions() {
+            return grantUriPermissions;
+        }
+
+        public boolean isForceUriPermissions() {
+            return forceUriPermissions;
+        }
+
+        public boolean isMultiProcess() {
+            return multiProcess;
+        }
+
+        public int getInitOrder() {
+            return initOrder;
+        }
+
+        public PatternMatcher[] getUriPermissionPatterns() {
+            return uriPermissionPatterns;
+        }
+
+        public PathPermission[] getPathPermissions() {
+            return pathPermissions;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeBoolean(this.exported);
+            dest.writeInt(this.flags);
+            dest.writeInt(this.order);
+            dest.writeString(this.authority);
+            dest.writeBoolean(this.isSyncable);
+            dest.writeString(this.readPermission);
+            dest.writeString(this.writePermission);
+            dest.writeBoolean(this.grantUriPermissions);
+            dest.writeBoolean(this.forceUriPermissions);
+            dest.writeBoolean(this.multiProcess);
+            dest.writeInt(this.initOrder);
+            dest.writeTypedArray(this.uriPermissionPatterns, flags);
+            dest.writeTypedArray(this.pathPermissions, flags);
+        }
+
+        public ParsedProvider() {
+        }
+
+        protected ParsedProvider(Parcel in) {
+            super(in);
+            this.exported = in.readByte() != 0;
+            this.flags = in.readInt();
+            this.order = in.readInt();
+            this.authority = TextUtils.safeIntern(in.readString());
+            this.isSyncable = in.readByte() != 0;
+            this.readPermission = TextUtils.safeIntern(in.readString());
+            this.writePermission = TextUtils.safeIntern(in.readString());
+            this.grantUriPermissions = in.readByte() != 0;
+            this.forceUriPermissions = in.readByte() != 0;
+            this.multiProcess = in.readByte() != 0;
+            this.initOrder = in.readInt();
+            this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+            this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+        }
+
+        public static final Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
+            @Override
+            public ParsedProvider createFromParcel(Parcel source) {
+                return new ParsedProvider(source);
+            }
+
+            @Override
+            public ParsedProvider[] newArray(int size) {
+                return new ParsedProvider[size];
+            }
+        };
+    }
+
+    public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> {
+
+        public String backgroundPermission;
+        private String group;
+        public int requestRes;
+        public int protectionLevel;
+        public boolean tree;
+
+        public ParsedPermissionGroup parsedPermissionGroup;
+
+        public void setName(String className) {
+            this.className = className;
+        }
+
+        public void setGroup(String group) {
+            this.group = TextUtils.safeIntern(group);
+        }
+
+        public String getGroup() {
+            return group;
+        }
+
+        public boolean isRuntime() {
+            return protectionLevel == PermissionInfo.PROTECTION_DANGEROUS;
+        }
+
+        public boolean isAppOp() {
+            return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+        }
+
+        @PermissionInfo.Protection
+        public int getProtection() {
+            return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+        }
+
+        public int getProtectionFlags() {
+            return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
+        }
+
+        public int calculateFootprint() {
+            int size = getName().length();
+            if (nonLocalizedLabel != null) {
+                size += nonLocalizedLabel.length();
+            }
+            return size;
+        }
+
+        public ParsedPermission() {
+        }
+
+        public ParsedPermission(ParsedPermission other) {
+            // TODO(b/135203078): Better way to copy this? Maybe refactor to the point where copy
+            //  isn't needed.
+            this.className = other.className;
+            this.icon = other.icon;
+            this.labelRes = other.labelRes;
+            this.nonLocalizedLabel = other.nonLocalizedLabel;
+            this.logo = other.logo;
+            this.banner = other.banner;
+            this.descriptionRes = other.descriptionRes;
+            this.enabled = other.enabled;
+            this.directBootAware = other.directBootAware;
+            this.flags = other.flags;
+            this.setSplitName(other.getSplitName());
+            this.setPackageName(other.getPackageName());
+
+            this.intents.addAll(other.intents);
+
+            if (other.metaData != null) {
+                this.metaData = new Bundle();
+                this.metaData.putAll(other.metaData);
+            }
+
+            this.backgroundPermission = other.backgroundPermission;
+            this.setGroup(other.group);
+            this.requestRes = other.requestRes;
+            this.protectionLevel = other.protectionLevel;
+            this.tree = other.tree;
+
+            this.parsedPermissionGroup = other.parsedPermissionGroup;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeString(this.backgroundPermission);
+            dest.writeString(this.group);
+            dest.writeInt(this.requestRes);
+            dest.writeInt(this.protectionLevel);
+            dest.writeBoolean(this.tree);
+            dest.writeParcelable(this.parsedPermissionGroup, flags);
+        }
+
+        protected ParsedPermission(Parcel in) {
+            super(in);
+            // We use the boot classloader for all classes that we load.
+            final ClassLoader boot = Object.class.getClassLoader();
+            this.backgroundPermission = in.readString();
+            this.group = TextUtils.safeIntern(in.readString());
+            this.requestRes = in.readInt();
+            this.protectionLevel = in.readInt();
+            this.tree = in.readBoolean();
+            this.parsedPermissionGroup = in.readParcelable(boot);
+        }
+
+        public static final Creator<ParsedPermission> CREATOR = new Creator<ParsedPermission>() {
+            @Override
+            public ParsedPermission createFromParcel(Parcel source) {
+                return new ParsedPermission(source);
+            }
+
+            @Override
+            public ParsedPermission[] newArray(int size) {
+                return new ParsedPermission[size];
+            }
+        };
+    }
+
+    public static class ParsedPermissionGroup extends ParsedComponent<ParsedIntentInfo> {
+
+        public int requestDetailResourceId;
+        public int backgroundRequestResourceId;
+        public int backgroundRequestDetailResourceId;
+
+        public int requestRes;
+        public int priority;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(this.requestDetailResourceId);
+            dest.writeInt(this.backgroundRequestResourceId);
+            dest.writeInt(this.backgroundRequestDetailResourceId);
+            dest.writeInt(this.requestRes);
+            dest.writeInt(this.priority);
+        }
+
+        public ParsedPermissionGroup() {
+        }
+
+        protected ParsedPermissionGroup(Parcel in) {
+            super(in);
+            this.requestDetailResourceId = in.readInt();
+            this.backgroundRequestResourceId = in.readInt();
+            this.backgroundRequestDetailResourceId = in.readInt();
+            this.requestRes = in.readInt();
+            this.priority = in.readInt();
+        }
+
+        public static final Creator<ParsedPermissionGroup> CREATOR =
+                new Creator<ParsedPermissionGroup>() {
+                    @Override
+                    public ParsedPermissionGroup createFromParcel(Parcel source) {
+                        return new ParsedPermissionGroup(source);
+                    }
+
+                    @Override
+                    public ParsedPermissionGroup[] newArray(int size) {
+                        return new ParsedPermissionGroup[size];
+                    }
+                };
+    }
+
+    public static class ParsedInstrumentation extends ParsedComponent<ParsedIntentInfo> {
+
+        private String targetPackage;
+        private String targetProcesses;
+        public boolean handleProfiling;
+        public boolean functionalTest;
+
+        public String sourceDir;
+        public String publicSourceDir;
+        public String[] splitNames;
+        public String[] splitSourceDirs;
+        public String[] splitPublicSourceDirs;
+        public SparseArray<int[]> splitDependencies;
+        public String dataDir;
+        public String deviceProtectedDataDir;
+        public String credentialProtectedDataDir;
+        public String primaryCpuAbi;
+        public String secondaryCpuAbi;
+        public String nativeLibraryDir;
+        public String secondaryNativeLibraryDir;
+
+        public ParsedInstrumentation() {
+        }
+
+        public void setTargetPackage(String targetPackage) {
+            this.targetPackage = TextUtils.safeIntern(targetPackage);
+        }
+
+        public String getTargetPackage() {
+            return targetPackage;
+        }
+
+        public void setTargetProcesses(String targetProcesses) {
+            this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+        }
+
+        public String getTargetProcesses() {
+            return targetProcesses;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeString(this.targetPackage);
+            dest.writeString(this.targetProcesses);
+            dest.writeBoolean(this.handleProfiling);
+            dest.writeBoolean(this.functionalTest);
+            dest.writeString(this.sourceDir);
+            dest.writeString(this.publicSourceDir);
+            dest.writeStringArray(this.splitNames);
+            dest.writeStringArray(this.splitSourceDirs);
+            dest.writeStringArray(this.splitPublicSourceDirs);
+            dest.writeSparseArray(this.splitDependencies);
+            dest.writeString(this.dataDir);
+            dest.writeString(this.deviceProtectedDataDir);
+            dest.writeString(this.credentialProtectedDataDir);
+            dest.writeString(this.primaryCpuAbi);
+            dest.writeString(this.secondaryCpuAbi);
+            dest.writeString(this.nativeLibraryDir);
+            dest.writeString(this.secondaryNativeLibraryDir);
+        }
+
+        protected ParsedInstrumentation(Parcel in) {
+            super(in);
+            // We use the boot classloader for all classes that we load.
+            final ClassLoader boot = Object.class.getClassLoader();
+            this.targetPackage = TextUtils.safeIntern(in.readString());
+            this.targetProcesses = TextUtils.safeIntern(in.readString());
+            this.handleProfiling = in.readByte() != 0;
+            this.functionalTest = in.readByte() != 0;
+            this.sourceDir = in.readString();
+            this.publicSourceDir = in.readString();
+            this.splitNames = in.createStringArray();
+            this.splitSourceDirs = in.createStringArray();
+            this.splitPublicSourceDirs = in.createStringArray();
+            this.splitDependencies = in.readSparseArray(boot);
+            this.dataDir = in.readString();
+            this.deviceProtectedDataDir = in.readString();
+            this.credentialProtectedDataDir = in.readString();
+            this.primaryCpuAbi = in.readString();
+            this.secondaryCpuAbi = in.readString();
+            this.nativeLibraryDir = in.readString();
+            this.secondaryNativeLibraryDir = in.readString();
+        }
+
+        public static final Creator<ParsedInstrumentation> CREATOR =
+                new Creator<ParsedInstrumentation>() {
+                    @Override
+                    public ParsedInstrumentation createFromParcel(Parcel source) {
+                        return new ParsedInstrumentation(source);
+                    }
+
+                    @Override
+                    public ParsedInstrumentation[] newArray(int size) {
+                        return new ParsedInstrumentation[size];
+                    }
+                };
+    }
+
+    public static ParsedActivity parseActivity(
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser, int flags, String[] outError,
+            boolean receiver, boolean hardwareAccelerated)
+            throws XmlPullParserException, IOException {
+
+        TypedArray sa = null;
+        boolean visibleToEphemeral;
+        boolean setExported;
+
+        int targetSdkVersion = parsingPackage.getTargetSdkVersion();
+        String packageName = parsingPackage.getPackageName();
+        String packageProcessName = parsingPackage.getProcessName();
+        ParsedActivity result = new ParsedActivity();
+
+        try {
+            sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
+
+            String tag = receiver ? "<receiver>" : "<activity>";
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_name, 0);
+            if (name == null) {
+                outError[0] = tag + " does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = tag + " invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestActivity_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivity_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivity_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivity_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivity_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            CharSequence pname;
+            if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(R.styleable.AndroidManifestActivity_process);
+            }
+
+            result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
+                    packageProcessName, pname,
+                    flags, separateProcesses, outError));
+
+            result.descriptionRes = sa.getResourceId(
+                    R.styleable.AndroidManifestActivity_description, 0);
+
+            result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivity_enabled, true);
+
+            setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
+            if (setExported) {
+                result.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported,
+                        false);
+            }
+
+            result.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
+
+            result.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
+                    parsingPackage.getUiOptions());
+
+            String parentName = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivity_parentActivityName,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            if (parentName != null) {
+                String parentClassName = ApkParseUtils.buildClassName(packageName, parentName);
+                if (parentClassName == null) {
+                    Log.e(TAG,
+                            "Activity " + result.className
+                                    + " specified invalid parentActivityName " +
+                                    parentName);
+                } else {
+                    result.parentActivityName = parentClassName;
+                }
+            }
+
+            String str;
+            str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
+            if (str == null) {
+                result.setPermission(parsingPackage.getPermission());
+            } else {
+                result.setPermission(str);
+            }
+
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestActivity_taskAffinity,
+                    Configuration.NATIVE_CONFIG_VERSION);
+            result.taskAffinity = PackageParser.buildTaskAffinityName(
+                    packageName,
+                    parsingPackage.getTaskAffinity(), str, outError);
+
+            result.setSplitName(
+                    sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0));
+
+            result.flags = 0;
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_multiprocess, false)) {
+                result.flags |= ActivityInfo.FLAG_MULTIPROCESS;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
+                result.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
+                result.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
+                result.flags |= ActivityInfo.FLAG_NO_HISTORY;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
+                result.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
+                result.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
+                result.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
+                    (parsingPackage.getFlags() & ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
+                            != 0)) {
+                result.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
+                    false)) {
+                result.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
+                    || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
+                result.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
+                result.flags |= ActivityInfo.FLAG_IMMERSIVE;
+            }
+
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
+                result.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
+            }
+
+            boolean directBootAware;
+
+            if (!receiver) {
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
+                        hardwareAccelerated)) {
+                    result.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
+                }
+
+                result.launchMode = sa.getInt(
+                        R.styleable.AndroidManifestActivity_launchMode,
+                        ActivityInfo.LAUNCH_MULTIPLE);
+                result.documentLaunchMode = sa.getInt(
+                        R.styleable.AndroidManifestActivity_documentLaunchMode,
+                        ActivityInfo.DOCUMENT_LAUNCH_NONE);
+                result.maxRecents = sa.getInt(
+                        R.styleable.AndroidManifestActivity_maxRecents,
+                        ActivityTaskManager.getDefaultAppRecentsLimitStatic());
+                result.configChanges = PackageParser.getActivityConfigChanges(
+                        sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+                        sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+                result.softInputMode = sa.getInt(
+                        R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
+
+                result.persistableMode = sa.getInteger(
+                        R.styleable.AndroidManifestActivity_persistableMode,
+                        ActivityInfo.PERSIST_ROOT_ONLY);
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
+                    result.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents,
+                        false)) {
+                    result.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
+                        false)) {
+                    result.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
+                    result.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+                }
+
+                int screenOrientation = sa.getInt(
+                        R.styleable.AndroidManifestActivity_screenOrientation,
+                        SCREEN_ORIENTATION_UNSPECIFIED);
+                result.screenOrientation = screenOrientation;
+
+                int resizeMode = getActivityResizeMode(parsingPackage, sa, screenOrientation);
+                result.resizeMode = resizeMode;
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+                        false)) {
+                    result.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
+                    result.flags |= FLAG_ALWAYS_FOCUSABLE;
+                }
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    result.setMaxAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+                                    0 /*default*/));
+                }
+
+                if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+                        == TypedValue.TYPE_FLOAT) {
+                    result.setMinAspectRatio(resizeMode,
+                            sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+                                    0 /*default*/));
+                }
+
+                result.lockTaskLaunchMode =
+                        sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
+
+                directBootAware = sa.getBoolean(
+                        R.styleable.AndroidManifestActivity_directBootAware,
+                        false);
+
+                result.requestedVrComponent =
+                        sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
+
+                result.rotationAnimation =
+                        sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation,
+                                ROTATION_ANIMATION_UNSPECIFIED);
+
+                result.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
+                        ActivityInfo.COLOR_MODE_DEFAULT);
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) {
+                    result.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) {
+                    result.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
+                }
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked,
+                        false)) {
+                    result.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
+                }
+            } else {
+                result.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                result.configChanges = 0;
+
+                if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
+                    result.flags |= ActivityInfo.FLAG_SINGLE_USER;
+                }
+                directBootAware = sa.getBoolean(
+                        R.styleable.AndroidManifestActivity_directBootAware,
+                        false);
+            }
+
+            result.directBootAware = directBootAware;
+
+            if (directBootAware) {
+                parsingPackage.setPartiallyDirectBootAware(true);
+            }
+
+            // can't make this final; we may set it later via meta-data
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                parsingPackage.setVisibleToInstantApps(true);
+            }
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+
+        if (receiver && (parsingPackage.getPrivateFlags()
+                & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
+            // A heavy-weight application can not have receives in its main process
+            if (result.getProcessName().equals(packageName)) {
+                outError[0] = "Heavy-weight applications can not have receivers in main process";
+                return null;
+            }
+        }
+
+        if (outError[0] != null) {
+            return null;
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
+                        result.className);
+                if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
+                        true /*allowGlobs*/,
+                        true /*allowAutoVerify*/, outError)) {
+                    return null;
+                }
+                if (intentInfo.countActions() == 0) {
+                    Slog.w(TAG, "No actions in intent filter at "
+                            + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                } else {
+                    result.order = Math.max(intentInfo.getOrder(), result.order);
+                    result.addIntent(intentInfo);
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : !receiver && isImplicitlyExposedIntent(intentInfo)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intentInfo.setVisibilityToInstantApp(visibility);
+                if (intentInfo.isVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intentInfo.isImplicitlyVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+                if (PackageParser.LOG_UNSAFE_BROADCASTS && receiver
+                        && (targetSdkVersion >= Build.VERSION_CODES.O)) {
+                    for (int i = 0; i < intentInfo.countActions(); i++) {
+                        final String action = intentInfo.getAction(i);
+                        if (action == null || !action.startsWith("android.")) continue;
+                        if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
+                            Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
+                                    + packageName + " as requested at: "
+                                    + parser.getPositionDescription());
+                        }
+                    }
+                }
+            } else if (!receiver && parser.getName().equals("preferred")) {
+                ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
+                        result.className);
+                if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
+                        false /*allowGlobs*/,
+                        false /*allowAutoVerify*/, outError)) {
+                    return null;
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : !receiver && isImplicitlyExposedIntent(intentInfo)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intentInfo.setVisibilityToInstantApp(visibility);
+                if (intentInfo.isVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intentInfo.isImplicitlyVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+
+                if (intentInfo.countActions() == 0) {
+                    Slog.w(TAG, "No actions in preferred at "
+                            + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                } else {
+                    parsingPackage.addPreferredActivityFilter(intentInfo);
+                }
+            } else if (parser.getName().equals("meta-data")) {
+                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
+                        result.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else if (!receiver && parser.getName().equals("layout")) {
+                result.windowLayout = parseLayout(res, parser);
+            } else {
+                if (!PackageParser.RIGID_PARSER) {
+                    Slog.w(TAG, "Problem in package " + parsingPackage.getBaseCodePath() + ":");
+                    if (receiver) {
+                        Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+                                + " at " + parsingPackage.getBaseCodePath() + " "
+                                + parser.getPositionDescription());
+                    } else {
+                        Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+                                + " at " + parsingPackage.getBaseCodePath() + " "
+                                + parser.getPositionDescription());
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    if (receiver) {
+                        outError[0] = "Bad element under <receiver>: " + parser.getName();
+                    } else {
+                        outError[0] = "Bad element under <activity>: " + parser.getName();
+                    }
+                    return null;
+                }
+            }
+        }
+
+        if (!setExported) {
+            result.exported = result.intents.size() > 0;
+        }
+
+        return result;
+    }
+
+    public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
+        return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intentInfo.hasAction(Intent.ACTION_SEND)
+                || intentInfo.hasAction(Intent.ACTION_SENDTO)
+                || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
+    }
+
+    public static int getActivityResizeMode(
+            ParsingPackage parsingPackage,
+            TypedArray sa,
+            int screenOrientation
+    ) {
+        int privateFlags = parsingPackage.getPrivateFlags();
+        final boolean appExplicitDefault = (privateFlags
+                & (ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
+                | ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0;
+
+        if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
+                || appExplicitDefault) {
+            // Activity or app explicitly set if it is resizeable or not;
+            final boolean appResizeable = (privateFlags
+                    & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0;
+            if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+                    appResizeable)) {
+                return ActivityInfo.RESIZE_MODE_RESIZEABLE;
+            } else {
+                return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+            }
+        }
+
+        if ((privateFlags
+                & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
+                != 0) {
+            // The activity or app didn't explicitly set the resizing option, however we want to
+            // make it resize due to the sdk version it is targeting.
+            return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+        }
+
+        // resize preference isn't set and target sdk version doesn't support resizing apps by
+        // default. For the app to be resizeable if it isn't fixed orientation or immersive.
+        if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+        } else {
+            return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+        }
+    }
+
+    public static ParsedService parseService(
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser, int flags, String[] outError
+    ) throws XmlPullParserException, IOException {
+        TypedArray sa = null;
+        boolean visibleToEphemeral;
+        boolean setExported;
+
+        String packageName = parsingPackage.getPackageName();
+        String packageProcessName = parsingPackage.getProcessName();
+        ParsedService result = new ParsedService();
+
+        try {
+            sa = res.obtainAttributes(parser,
+                    R.styleable.AndroidManifestService);
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestService_name, 0);
+            if (name == null) {
+                outError[0] = "<service> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<service> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestService_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestService_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestService_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestService_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestService_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            CharSequence pname;
+            if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(R.styleable.AndroidManifestService_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(R.styleable.AndroidManifestService_process);
+            }
+
+            result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
+                    packageProcessName, pname,
+                    flags, separateProcesses, outError));
+
+            result.descriptionRes = sa.getResourceId(
+                    R.styleable.AndroidManifestService_description, 0);
+
+            result.enabled = sa.getBoolean(R.styleable.AndroidManifestService_enabled, true);
+
+            setExported = sa.hasValue(
+                    R.styleable.AndroidManifestService_exported);
+            if (setExported) {
+                result.exported = sa.getBoolean(
+                        R.styleable.AndroidManifestService_exported, false);
+            }
+
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestService_permission, 0);
+            if (str == null) {
+                result.setPermission(parsingPackage.getPermission());
+            } else {
+                result.setPermission(str);
+            }
+
+            result.setSplitName(
+                    sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0));
+
+            result.foregroundServiceType = sa.getInt(
+                    R.styleable.AndroidManifestService_foregroundServiceType,
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+
+            result.flags = 0;
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestService_stopWithTask,
+                    false)) {
+                result.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
+            }
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestService_isolatedProcess,
+                    false)) {
+                result.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
+            }
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestService_externalService,
+                    false)) {
+                result.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
+            }
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestService_useAppZygote,
+                    false)) {
+                result.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+            }
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestService_singleUser,
+                    false)) {
+                result.flags |= ServiceInfo.FLAG_SINGLE_USER;
+            }
+
+            result.directBootAware = sa.getBoolean(
+                    R.styleable.AndroidManifestService_directBootAware,
+                    false);
+            if (result.directBootAware) {
+                parsingPackage.setPartiallyDirectBootAware(true);
+            }
+
+            visibleToEphemeral = sa.getBoolean(
+                    R.styleable.AndroidManifestService_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                parsingPackage.setVisibleToInstantApps(true);
+            }
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        if (parsingPackage.cantSaveState()) {
+            // A heavy-weight application can not have services in its main process
+            // We can do direct compare because we intern all strings.
+            if (Objects.equals(result.getProcessName(), parsingPackage.getPackageName())) {
+                outError[0] = "Heavy-weight applications can not have services in main process";
+                return null;
+            }
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ParsedServiceIntentInfo intent = new ParsedServiceIntentInfo(packageName,
+                        result.className);
+                if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
+                        false /*allowAutoVerify*/,
+                        outError)) {
+                    return null;
+                }
+                if (visibleToEphemeral) {
+                    intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
+                    result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                result.order = Math.max(intent.getOrder(), result.order);
+                result.intents.add(intent);
+            } else if (parser.getName().equals("meta-data")) {
+                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
+                        result.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else {
+                if (!PackageParser.RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <service>: "
+                            + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <service>: " + parser.getName();
+                    return null;
+                }
+            }
+        }
+
+        if (!setExported) {
+            result.exported = result.intents.size() > 0;
+        }
+
+        return result;
+    }
+
+    public static ParsedProvider parseProvider(
+            String[] separateProcesses,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser, int flags, String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = null;
+        String cpname;
+        boolean visibleToEphemeral;
+
+        int targetSdkVersion = parsingPackage.getTargetSdkVersion();
+        String packageName = parsingPackage.getPackageName();
+        String packageProcessName = parsingPackage.getProcessName();
+        ParsedProvider result = new ParsedProvider();
+
+        try {
+            sa = res.obtainAttributes(parser,
+                    R.styleable.AndroidManifestProvider);
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_name, 0);
+            if (name == null) {
+                outError[0] = "<provider> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<provider> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestProvider_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestProvider_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestProvider_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestProvider_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestProvider_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            CharSequence pname;
+            if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+                pname = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_process,
+                        Configuration.NATIVE_CONFIG_VERSION);
+            } else {
+                // Some older apps have been seen to use a resource reference
+                // here that on older builds was ignored (with a warning).  We
+                // need to continue to do this for them so they don't break.
+                pname = sa.getNonResourceString(R.styleable.AndroidManifestProvider_process);
+            }
+
+            result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
+                    packageProcessName, pname,
+                    flags, separateProcesses, outError));
+
+            result.descriptionRes = sa.getResourceId(
+                    R.styleable.AndroidManifestProvider_description, 0);
+
+            result.enabled = sa.getBoolean(R.styleable.AndroidManifestProvider_enabled, true);
+
+            boolean providerExportedDefault = false;
+
+            if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                // For compatibility, applications targeting API level 16 or lower
+                // should have their content providers exported by default, unless they
+                // specify otherwise.
+                providerExportedDefault = true;
+            }
+
+            result.exported = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_exported,
+                    providerExportedDefault);
+
+            cpname = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_authorities, 0);
+
+            result.isSyncable = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_syncable,
+                    false);
+
+            String permission = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_permission, 0);
+            String str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_readPermission, 0);
+            if (str == null) {
+                str = permission;
+            }
+            if (str == null) {
+                result.setReadPermission(parsingPackage.getPermission());
+            } else {
+                result.setReadPermission(str);
+            }
+            str = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestProvider_writePermission, 0);
+            if (str == null) {
+                str = permission;
+            }
+            if (str == null) {
+                result.setWritePermission(parsingPackage.getPermission());
+            } else {
+                result.setWritePermission(str);
+            }
+
+            result.grantUriPermissions = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_grantUriPermissions,
+                    false);
+
+            result.forceUriPermissions = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_forceUriPermissions,
+                    false);
+
+            result.multiProcess = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_multiprocess,
+                    false);
+
+            result.initOrder = sa.getInt(
+                    R.styleable.AndroidManifestProvider_initOrder,
+                    0);
+
+            result.setSplitName(
+                    sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0));
+
+            result.flags = 0;
+
+            if (sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_singleUser,
+                    false)) {
+                result.flags |= ProviderInfo.FLAG_SINGLE_USER;
+            }
+
+            result.directBootAware = sa.getBoolean(
+                    R.styleable.AndroidManifestProvider_directBootAware,
+                    false);
+            if (result.directBootAware) {
+                parsingPackage.setPartiallyDirectBootAware(true);
+            }
+
+            visibleToEphemeral =
+                    sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+            if (visibleToEphemeral) {
+                result.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                parsingPackage.setVisibleToInstantApps(true);
+            }
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        if ((parsingPackage.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+                != 0) {
+            // A heavy-weight application can not have providers in its main process
+            if (result.getProcessName().equals(packageName)) {
+                outError[0] = "Heavy-weight applications can not have providers in main process";
+                return null;
+            }
+        }
+
+        if (cpname == null) {
+            outError[0] = "<provider> does not include authorities attribute";
+            return null;
+        }
+        if (cpname.length() <= 0) {
+            outError[0] = "<provider> has empty authorities attribute";
+            return null;
+        }
+        result.setAuthority(cpname);
+
+        if (!parseProviderTags(parsingPackage, res, parser, visibleToEphemeral, result, outError)) {
+            return null;
+        }
+
+        return result;
+    }
+
+    public static ParsedQueriesIntentInfo parsedParsedQueriesIntentInfo(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        ParsedQueriesIntentInfo intentInfo = new ParsedQueriesIntentInfo(
+                parsingPackage.getPackageName(),
+                null
+        );
+        if (!parseIntentInfo(
+                intentInfo,
+                parsingPackage,
+                res,
+                parser,
+                true /*allowGlobs*/,
+                true /*allowAutoVerify*/,
+                outError
+        )) {
+            return null;
+        }
+        return intentInfo;
+    }
+
+    private static boolean parseProviderTags(
+            ParsingPackage parsingPackage,
+            Resources res, XmlResourceParser parser,
+            boolean visibleToEphemeral, ParsedProvider outInfo, String[] outError)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("intent-filter")) {
+                ParsedProviderIntentInfo intent = new ParsedProviderIntentInfo(
+                        parsingPackage.getPackageName(), outInfo.className);
+                if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
+                        false /*allowAutoVerify*/,
+                        outError)) {
+                    return false;
+                }
+                if (visibleToEphemeral) {
+                    intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
+                    outInfo.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                outInfo.order = Math.max(intent.getOrder(), outInfo.order);
+                outInfo.intents.add(intent);
+
+            } else if (parser.getName().equals("meta-data")) {
+                Bundle metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
+                        outInfo.metaData, outError);
+                if (metaData == null) {
+                    return false;
+                } else {
+                    outInfo.metaData = metaData;
+                }
+
+            } else if (parser.getName().equals("grant-uri-permission")) {
+                TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestGrantUriPermission);
+
+                PatternMatcher pa = null;
+
+                String str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestGrantUriPermission_path, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+                if (str != null) {
+                    pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                sa.recycle();
+
+                if (pa != null) {
+                    if (outInfo.uriPermissionPatterns == null) {
+                        outInfo.uriPermissionPatterns = new PatternMatcher[1];
+                        outInfo.uriPermissionPatterns[0] = pa;
+                    } else {
+                        final int N = outInfo.uriPermissionPatterns.length;
+                        PatternMatcher[] newp = new PatternMatcher[N + 1];
+                        System.arraycopy(outInfo.uriPermissionPatterns, 0, newp, 0, N);
+                        newp[N] = pa;
+                        outInfo.uriPermissionPatterns = newp;
+                    }
+                    outInfo.grantUriPermissions = true;
+                } else {
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG, "Unknown element under <path-permission>: "
+                                + parser.getName() + " at " + parsingPackage.getBaseCodePath()
+                                + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                        return false;
+                    }
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else if (parser.getName().equals("path-permission")) {
+                TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestPathPermission);
+
+                PathPermission pa = null;
+
+                String permission = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_permission, 0);
+                String readPermission = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_readPermission, 0);
+                if (readPermission == null) {
+                    readPermission = permission;
+                }
+                String writePermission = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_writePermission, 0);
+                if (writePermission == null) {
+                    writePermission = permission;
+                }
+
+                boolean havePerm = false;
+                if (readPermission != null) {
+                    readPermission = readPermission.intern();
+                    havePerm = true;
+                }
+                if (writePermission != null) {
+                    writePermission = writePermission.intern();
+                    havePerm = true;
+                }
+
+                if (!havePerm) {
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG, "No readPermission or writePermssion for <path-permission>: "
+                                + parser.getName() + " at " + parsingPackage.getBaseCodePath()
+                                + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    } else {
+                        outError[0] = "No readPermission or writePermssion for <path-permission>";
+                        return false;
+                    }
+                }
+
+                String path = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_path, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_pathPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
+                }
+
+                path = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+                if (path != null) {
+                    pa = new PathPermission(path,
+                            PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission);
+                }
+
+                sa.recycle();
+
+                if (pa != null) {
+                    if (outInfo.pathPermissions == null) {
+                        outInfo.pathPermissions = new PathPermission[1];
+                        outInfo.pathPermissions[0] = pa;
+                    } else {
+                        final int N = outInfo.pathPermissions.length;
+                        PathPermission[] newp = new PathPermission[N + 1];
+                        System.arraycopy(outInfo.pathPermissions, 0, newp, 0, N);
+                        newp[N] = pa;
+                        outInfo.pathPermissions = newp;
+                    }
+                } else {
+                    if (!PackageParser.RIGID_PARSER) {
+                        Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+                                + parser.getName() + " at " + parsingPackage.getBaseCodePath()
+                                + " "
+                                + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                if (!PackageParser.RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <provider>: "
+                            + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <provider>: " + parser.getName();
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public static ParsedActivity parseActivityAlias(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError)
+            throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestActivityAlias);
+
+        String targetActivity = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestActivityAlias_targetActivity,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (targetActivity == null) {
+            outError[0] = "<activity-alias> does not specify android:targetActivity";
+            sa.recycle();
+            return null;
+        }
+
+        String packageName = parsingPackage.getPackageName();
+        targetActivity = ApkParseUtils.buildClassName(packageName, targetActivity);
+        if (targetActivity == null) {
+            outError[0] = "Empty class name in package " + packageName;
+            sa.recycle();
+            return null;
+        }
+
+        ParsedActivity target = null;
+
+        List<ParsedActivity> activities = parsingPackage.getActivities();
+        final int NA = activities.size();
+        for (int i = 0; i < NA; i++) {
+            ParsedActivity t = activities.get(i);
+            if (targetActivity.equals(t.className)) {
+                target = t;
+                break;
+            }
+        }
+
+        if (target == null) {
+            outError[0] = "<activity-alias> target activity " + targetActivity
+                    + " not found in manifest with activities = " + parsingPackage.getActivities()
+                    + ", parsedActivities = " + activities;
+            sa.recycle();
+            return null;
+        }
+
+        ParsedActivity result = new ParsedActivity();
+        result.setPackageNameInternal(target.getPackageName());
+        result.targetActivity = targetActivity;
+        result.configChanges = target.configChanges;
+        result.flags = target.flags;
+        result.privateFlags = target.privateFlags;
+        result.icon = target.icon;
+        result.logo = target.logo;
+        result.banner = target.banner;
+        result.labelRes = target.labelRes;
+        result.nonLocalizedLabel = target.nonLocalizedLabel;
+        result.launchMode = target.launchMode;
+        result.lockTaskLaunchMode = target.lockTaskLaunchMode;
+        result.descriptionRes = target.descriptionRes;
+        result.screenOrientation = target.screenOrientation;
+        result.taskAffinity = target.taskAffinity;
+        result.theme = target.theme;
+        result.softInputMode = target.softInputMode;
+        result.uiOptions = target.uiOptions;
+        result.parentActivityName = target.parentActivityName;
+        result.maxRecents = target.maxRecents;
+        result.windowLayout = target.windowLayout;
+        result.resizeMode = target.resizeMode;
+        result.maxAspectRatio = target.maxAspectRatio;
+        result.hasMaxAspectRatio = target.hasMaxAspectRatio;
+        result.minAspectRatio = target.minAspectRatio;
+        result.hasMinAspectRatio = target.hasMinAspectRatio;
+        result.requestedVrComponent = target.requestedVrComponent;
+        result.directBootAware = target.directBootAware;
+
+        result.setProcessName(parsingPackage.getAppInfoProcessName(), target.getProcessName());
+
+        // Not all attributes from the target ParsedActivity are copied to the alias.
+        // Careful when adding an attribute and determine whether or not it should be copied.
+//        result.enabled = target.enabled;
+//        result.exported = target.exported;
+//        result.permission = target.permission;
+//        result.splitName = target.splitName;
+//        result.documentLaunchMode = target.documentLaunchMode;
+//        result.persistableMode = target.persistableMode;
+//        result.rotationAnimation = target.rotationAnimation;
+//        result.colorMode = target.colorMode;
+//        result.intents.addAll(target.intents);
+//        result.order = target.order;
+//        result.metaData = target.metaData;
+
+        String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivityAlias_name,
+                0);
+        if (name == null) {
+            outError[0] = "<activity-alias> does not specify android:name";
+            return null;
+        } else {
+            String className = ApkParseUtils.buildClassName(packageName, name);
+            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                outError[0] = "<activity-alias> invalid android:name";
+                return null;
+            } else if (className == null) {
+                outError[0] = "Empty class name in package " + packageName;
+                return null;
+            }
+
+            result.className = className;
+        }
+
+        int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                R.styleable.AndroidManifestActivityAlias_roundIcon, 0) : 0;
+        if (roundIconVal != 0) {
+            result.icon = roundIconVal;
+            result.nonLocalizedLabel = null;
+        } else {
+            int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_icon, 0);
+            if (iconVal != 0) {
+                result.icon = iconVal;
+                result.nonLocalizedLabel = null;
+            }
+        }
+
+        int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_logo, 0);
+        if (logoVal != 0) {
+            result.logo = logoVal;
+        }
+
+        int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_banner, 0);
+        if (bannerVal != 0) {
+            result.banner = bannerVal;
+        }
+
+        TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivityAlias_label);
+        if (v != null && (result.labelRes = v.resourceId) == 0) {
+            result.nonLocalizedLabel = v.coerceToString();
+        }
+
+        result.setPackageNameInternal(packageName);
+
+        result.descriptionRes = sa.getResourceId(
+                R.styleable.AndroidManifestActivityAlias_description, 0);
+
+        result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivityAlias_enabled, true);
+
+        final boolean setExported = sa.hasValue(
+                R.styleable.AndroidManifestActivityAlias_exported);
+        if (setExported) {
+            result.exported = sa.getBoolean(
+                    R.styleable.AndroidManifestActivityAlias_exported, false);
+        }
+
+        String str;
+        str = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestActivityAlias_permission, 0);
+        if (str != null) {
+            result.setPermission(str);
+        }
+
+        String parentName = sa.getNonConfigurationString(
+                R.styleable.AndroidManifestActivityAlias_parentActivityName,
+                Configuration.NATIVE_CONFIG_VERSION);
+        if (parentName != null) {
+            String parentClassName = ApkParseUtils.buildClassName(result.getPackageName(),
+                    parentName);
+            if (parentClassName == null) {
+                Log.e(TAG, "Activity alias " + result.className +
+                        " specified invalid parentActivityName " + parentName);
+                outError[0] = null;
+            } else {
+                result.parentActivityName = parentClassName;
+            }
+        }
+
+        // TODO add visibleToInstantApps attribute to activity alias
+        final boolean visibleToEphemeral =
+                ((result.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
+
+        sa.recycle();
+
+        if (outError[0] != null) {
+            return null;
+        }
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("intent-filter")) {
+                ParsedActivityIntentInfo intent = new ParsedActivityIntentInfo(packageName,
+                        result.className);
+                if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
+                        true /*allowAutoVerify*/, outError)) {
+                    return null;
+                }
+                if (intent.countActions() == 0) {
+                    Slog.w(TAG, "No actions in intent filter at "
+                            + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                } else {
+                    result.order = Math.max(intent.getOrder(), result.order);
+                    result.addIntent(intent);
+                }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                final int visibility = visibleToEphemeral
+                        ? IntentFilter.VISIBILITY_EXPLICIT
+                        : isImplicitlyExposedIntent(intent)
+                                ? IntentFilter.VISIBILITY_IMPLICIT
+                                : IntentFilter.VISIBILITY_NONE;
+                intent.setVisibilityToInstantApp(visibility);
+                if (intent.isVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+                }
+                if (intent.isImplicitlyVisibleToInstantApp()) {
+                    result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+                }
+            } else if (tagName.equals("meta-data")) {
+                if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
+                        result.metaData,
+                        outError)) == null) {
+                    return null;
+                }
+            } else {
+                if (!PackageParser.RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under <activity-alias>: " + tagName
+                            + " at " + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under <activity-alias>: " + tagName;
+                    return null;
+                }
+            }
+        }
+
+        if (!setExported) {
+            result.exported = result.intents.size() > 0;
+        }
+
+        return result;
+    }
+
+    public static ParsedPermission parsePermission(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = null;
+        String packageName = parsingPackage.getPackageName();
+        ParsedPermission result = new ParsedPermission();
+
+        try {
+            sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
+
+            String name = sa.getNonConfigurationString(R.styleable.AndroidManifestPermission_name,
+                    0);
+            if (name == null) {
+                outError[0] = "<permission> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<permission> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestPermission_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermission_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermission_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermission_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermission_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            result.descriptionRes = sa.getResourceId(
+                    R.styleable.AndroidManifestPermission_description, 0);
+
+            if (sa.hasValue(
+                    R.styleable.AndroidManifestPermission_backgroundPermission)) {
+                if ("android".equals(packageName)) {
+                    result.backgroundPermission = sa.getNonResourceString(
+                            R.styleable
+                                    .AndroidManifestPermission_backgroundPermission);
+                } else {
+                    Slog.w(TAG, packageName + " defines a background permission. Only the "
+                            + "'android' package can do that.");
+                }
+            }
+
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            result.setGroup(sa.getNonResourceString(
+                    R.styleable.AndroidManifestPermission_permissionGroup));
+
+            result.requestRes = sa.getResourceId(
+                    R.styleable.AndroidManifestPermission_request, 0);
+
+            result.protectionLevel = sa.getInt(
+                    R.styleable.AndroidManifestPermission_protectionLevel,
+                    PermissionInfo.PROTECTION_NORMAL);
+
+            result.flags = sa.getInt(
+                    R.styleable.AndroidManifestPermission_permissionFlags, 0);
+
+            // For now only platform runtime permissions can be restricted
+            if (!result.isRuntime() || !"android".equals(result.getPackageName())) {
+                result.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
+                result.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+            } else {
+                // The platform does not get to specify conflicting permissions
+                if ((result.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+                        && (result.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+                    throw new IllegalStateException("Permission cannot be both soft and hard"
+                            + " restricted: " + result.getName());
+                }
+            }
+
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        if (result.protectionLevel == -1) {
+            outError[0] = "<permission> does not specify protectionLevel";
+            return null;
+        }
+
+        result.protectionLevel = PermissionInfo.fixProtectionLevel(result.protectionLevel);
+
+        if (result.getProtectionFlags() != 0) {
+            if ((result.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
+                    && (result.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+                    == 0
+                    && (result.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) !=
+                    PermissionInfo.PROTECTION_SIGNATURE) {
+                outError[0] = "<permission>  protectionLevel specifies a non-instant flag but is "
+                        + "not based on signature type";
+                return null;
+            }
+        }
+
+        boolean success = parseAllMetaData(parsingPackage, res, parser,
+                "<permission>", result, outError);
+        if (!success || outError[0] != null) {
+            return null;
+        }
+
+        return result;
+    }
+
+    public static ParsedPermission parsePermissionTree(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = null;
+        String packageName = parsingPackage.getPackageName();
+        ParsedPermission result = new ParsedPermission();
+
+        try {
+            sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
+
+            String name = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPermissionTree_name, 0);
+            if (name == null) {
+                outError[0] = "<permission-tree> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<permission-tree> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionTree_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionTree_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        int index = result.getName().indexOf('.');
+        if (index > 0) {
+            index = result.getName().indexOf('.', index + 1);
+        }
+        if (index < 0) {
+            outError[0] =
+                    "<permission-tree> name has less than three segments: " + result.getName();
+            return null;
+        }
+
+        result.descriptionRes = 0;
+        result.requestRes = 0;
+        result.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
+        result.tree = true;
+
+        boolean success = parseAllMetaData(parsingPackage, res, parser,
+                "<permission-tree>", result, outError);
+        if (!success || outError[0] != null) {
+            return null;
+        }
+
+        return result;
+    }
+
+    public static ParsedPermissionGroup parsePermissionGroup(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = null;
+        String packageName = parsingPackage.getPackageName();
+        ParsedPermissionGroup result = new ParsedPermissionGroup();
+
+        try {
+            sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
+
+            String name = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestPermissionGroup_name, 0);
+            if (name == null) {
+                outError[0] = "<permission> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<permission> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionGroup_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionGroup_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            result.descriptionRes = sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionGroup_description, 0);
+
+            result.requestDetailResourceId = sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+            result.backgroundRequestResourceId = sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
+                    0);
+            result.backgroundRequestDetailResourceId = sa.getResourceId(
+                    R.styleable
+                            .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
+
+            result.requestRes = sa.getResourceId(
+                    R.styleable.AndroidManifestPermissionGroup_request, 0);
+            result.flags = sa.getInt(
+                    R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,
+                    0);
+            result.priority = sa.getInt(
+                    R.styleable.AndroidManifestPermissionGroup_priority, 0);
+
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        boolean success = parseAllMetaData(parsingPackage, res, parser,
+                "<permission-group>", result, outError);
+        if (!success || outError[0] != null) {
+            return null;
+        }
+
+        return result;
+    }
+
+    public static ParsedInstrumentation parseInstrumentation(
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        TypedArray sa = null;
+        String packageName = parsingPackage.getPackageName();
+        ParsedInstrumentation result = new ParsedInstrumentation();
+
+        try {
+            sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
+
+            // TODO(b/135203078): Re-share all of the configuration for this. ParseComponentArgs was
+            //  un-used for this, but can be adjusted and re-added to share all the initial result
+            //  parsing for icon/logo/name/etc in all of these parse methods.
+            String name = sa.getNonConfigurationString(
+                    R.styleable.AndroidManifestInstrumentation_name, 0);
+            if (name == null) {
+                outError[0] = "<instrumentation> does not specify android:name";
+                return null;
+            } else {
+                String className = ApkParseUtils.buildClassName(packageName, name);
+                if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+                    outError[0] = "<instrumentation> invalid android:name";
+                    return null;
+                } else if (className == null) {
+                    outError[0] = "Empty class name in package " + packageName;
+                    return null;
+                }
+
+                result.className = className;
+            }
+
+            int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                    R.styleable.AndroidManifestInstrumentation_roundIcon, 0) : 0;
+            if (roundIconVal != 0) {
+                result.icon = roundIconVal;
+                result.nonLocalizedLabel = null;
+            } else {
+                int iconVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_icon, 0);
+                if (iconVal != 0) {
+                    result.icon = iconVal;
+                    result.nonLocalizedLabel = null;
+                }
+            }
+
+            int logoVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_logo, 0);
+            if (logoVal != 0) {
+                result.logo = logoVal;
+            }
+
+            int bannerVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_banner, 0);
+            if (bannerVal != 0) {
+                result.banner = bannerVal;
+            }
+
+            TypedValue v = sa.peekValue(R.styleable.AndroidManifestInstrumentation_label);
+            if (v != null && (result.labelRes = v.resourceId) == 0) {
+                result.nonLocalizedLabel = v.coerceToString();
+            }
+
+            result.setPackageNameInternal(packageName);
+
+            String str;
+            // Note: don't allow this value to be a reference to a resource
+            // that may change.
+            str = sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage);
+            result.setTargetPackage(str);
+
+            str = sa.getNonResourceString(
+                    R.styleable.AndroidManifestInstrumentation_targetProcesses);
+            result.setTargetProcesses(str);
+            result.handleProfiling = sa.getBoolean(
+                    R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
+            result.functionalTest = sa.getBoolean(
+                    R.styleable.AndroidManifestInstrumentation_functionalTest, false);
+
+        } finally {
+            if (sa != null) {
+                sa.recycle();
+            }
+        }
+
+        boolean success = parseAllMetaData(parsingPackage, res, parser,
+                "<instrumentation>", result, outError);
+        if (!success || outError[0] != null) {
+            return null;
+        }
+
+        return result;
+    }
+
+    public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) {
+        TypedArray sw = res.obtainAttributes(attrs,
+                R.styleable.AndroidManifestLayout);
+        int width = -1;
+        float widthFraction = -1f;
+        int height = -1;
+        float heightFraction = -1f;
+        final int widthType = sw.getType(
+                R.styleable.AndroidManifestLayout_defaultWidth);
+        if (widthType == TypedValue.TYPE_FRACTION) {
+            widthFraction = sw.getFraction(
+                    R.styleable.AndroidManifestLayout_defaultWidth,
+                    1, 1, -1);
+        } else if (widthType == TypedValue.TYPE_DIMENSION) {
+            width = sw.getDimensionPixelSize(
+                    R.styleable.AndroidManifestLayout_defaultWidth,
+                    -1);
+        }
+        final int heightType = sw.getType(
+                R.styleable.AndroidManifestLayout_defaultHeight);
+        if (heightType == TypedValue.TYPE_FRACTION) {
+            heightFraction = sw.getFraction(
+                    R.styleable.AndroidManifestLayout_defaultHeight,
+                    1, 1, -1);
+        } else if (heightType == TypedValue.TYPE_DIMENSION) {
+            height = sw.getDimensionPixelSize(
+                    R.styleable.AndroidManifestLayout_defaultHeight,
+                    -1);
+        }
+        int gravity = sw.getInt(
+                R.styleable.AndroidManifestLayout_gravity,
+                Gravity.CENTER);
+        int minWidth = sw.getDimensionPixelSize(
+                R.styleable.AndroidManifestLayout_minWidth,
+                -1);
+        int minHeight = sw.getDimensionPixelSize(
+                R.styleable.AndroidManifestLayout_minHeight,
+                -1);
+        sw.recycle();
+        return new ActivityInfo.WindowLayout(width, widthFraction,
+                height, heightFraction, gravity, minWidth, minHeight);
+    }
+
+    public static boolean parseIntentInfo(
+            ParsedIntentInfo intentInfo,
+            ParsingPackage parsingPackage,
+            Resources res, XmlResourceParser parser, boolean allowGlobs,
+            boolean allowAutoVerify, String[] outError
+    ) throws XmlPullParserException, IOException {
+        TypedArray sa = res.obtainAttributes(parser,
+                R.styleable.AndroidManifestIntentFilter);
+
+        int priority = sa.getInt(
+                R.styleable.AndroidManifestIntentFilter_priority, 0);
+        intentInfo.setPriority(priority);
+
+        int order = sa.getInt(
+                R.styleable.AndroidManifestIntentFilter_order, 0);
+        intentInfo.setOrder(order);
+
+        TypedValue v = sa.peekValue(
+                R.styleable.AndroidManifestIntentFilter_label);
+        if (v != null && (intentInfo.labelRes = v.resourceId) == 0) {
+            intentInfo.nonLocalizedLabel = v.coerceToString();
+        }
+
+        int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
+                R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
+        if (roundIconVal != 0) {
+            intentInfo.icon = roundIconVal;
+        } else {
+            intentInfo.icon = sa.getResourceId(
+                    R.styleable.AndroidManifestIntentFilter_icon, 0);
+        }
+
+        if (allowAutoVerify) {
+            intentInfo.setAutoVerify(sa.getBoolean(
+                    R.styleable.AndroidManifestIntentFilter_autoVerify,
+                    false));
+        }
+
+        sa.recycle();
+
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String nodeName = parser.getName();
+            if (nodeName.equals("action")) {
+                String value = parser.getAttributeValue(
+                        PackageParser.ANDROID_RESOURCES, "name");
+                if (TextUtils.isEmpty(value)) {
+                    outError[0] = "No value supplied for <android:name>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+                intentInfo.addAction(value);
+            } else if (nodeName.equals("category")) {
+                String value = parser.getAttributeValue(
+                        PackageParser.ANDROID_RESOURCES, "name");
+                if (TextUtils.isEmpty(value)) {
+                    outError[0] = "No value supplied for <android:name>";
+                    return false;
+                }
+                XmlUtils.skipCurrentTag(parser);
+
+                intentInfo.addCategory(value);
+
+            } else if (nodeName.equals("data")) {
+                sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestData);
+
+                String str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_mimeType, 0);
+                if (str != null) {
+                    try {
+                        intentInfo.addRawDataType(str);
+                    } catch (IntentFilter.MalformedMimeTypeException e) {
+                        outError[0] = e.toString();
+                        sa.recycle();
+                        return false;
+                    }
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_scheme, 0);
+                if (str != null) {
+                    intentInfo.addDataScheme(str);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_ssp, 0);
+                if (str != null) {
+                    intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_sspPrefix, 0);
+                if (str != null) {
+                    intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_sspPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "sspPattern not allowed here; ssp must be literal";
+                        return false;
+                    }
+                    intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                String host = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_host, 0);
+                String port = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_port, 0);
+                if (host != null) {
+                    intentInfo.addDataAuthority(host, port);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_path, 0);
+                if (str != null) {
+                    intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_pathPrefix, 0);
+                if (str != null) {
+                    intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_pathPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "pathPattern not allowed here; path must be literal";
+                        return false;
+                    }
+                    intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+                }
+
+                str = sa.getNonConfigurationString(
+                        R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+                if (str != null) {
+                    if (!allowGlobs) {
+                        outError[0] = "pathAdvancedPattern not allowed here; path must be literal";
+                        return false;
+                    }
+                    intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+                }
+
+                sa.recycle();
+                XmlUtils.skipCurrentTag(parser);
+            } else if (!PackageParser.RIGID_PARSER) {
+                Slog.w(TAG, "Unknown element under <intent-filter>: "
+                        + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+                        + parser.getPositionDescription());
+                XmlUtils.skipCurrentTag(parser);
+            } else {
+                outError[0] = "Bad element under <intent-filter>: " + parser.getName();
+                return false;
+            }
+        }
+
+        intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+
+        if (PackageParser.DEBUG_PARSER) {
+            final StringBuilder cats = new StringBuilder("Intent d=");
+            cats.append(intentInfo.hasDefault);
+            cats.append(", cat=");
+
+            final Iterator<String> it = intentInfo.categoriesIterator();
+            if (it != null) {
+                while (it.hasNext()) {
+                    cats.append(' ');
+                    cats.append(it.next());
+                }
+            }
+            Slog.d(TAG, cats.toString());
+        }
+
+        return true;
+    }
+
+    private static boolean parseAllMetaData(
+            ParsingPackage parsingPackage,
+            Resources res, XmlResourceParser parser, String tag,
+            ParsedComponent outInfo,
+            String[] outError
+    ) throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals("meta-data")) {
+                if ((outInfo.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
+                        outInfo.metaData, outError)) == null) {
+                    return false;
+                }
+            } else {
+                if (!PackageParser.RIGID_PARSER) {
+                    Slog.w(TAG, "Unknown element under " + tag + ": "
+                            + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+                            + parser.getPositionDescription());
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                } else {
+                    outError[0] = "Bad element under " + tag + ": " + parser.getName();
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public static boolean isImplicitlyExposedIntent(IntentFilter intent) {
+        return intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                || intent.hasAction(Intent.ACTION_SEND)
+                || intent.hasAction(Intent.ACTION_SENDTO)
+                || intent.hasAction(Intent.ACTION_SEND_MULTIPLE);
+    }
+}
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
new file mode 100644
index 0000000..363cf80
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -0,0 +1,3213 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.pm.parsing;
+
+import static android.os.Build.VERSION_CODES.DONUT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * The backing data for a package that was parsed from disk.
+ *
+ * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
+ * TODO(b/135203078): Field nullability annotations
+ * TODO(b/135203078): Convert = 1 fields into Booleans
+ * TODO(b/135203078): Make all lists nullable and Collections.unmodifiable immutable when returned.
+ *   Prefer add/set methods if adding is necessary.
+ * TODO(b/135203078): Consider comments to disable auto-format and single-line, single-space all the
+ *   get/set methods to make this class far more compact. Maybe even separate some logic into parent
+ *   classes, assuming there is no overhead.
+ * TODO(b/135203078): Copy documentation from PackageParser#Package for the relevant fields included
+ *   here. Should clarify and clean up any differences. Also consider renames if it helps make
+ *   things clearer.
+ * TODO(b/135203078): Intern all possibl e String values? Initial refactor just mirrored old
+ *   behavior.
+ *
+ * @hide
+ */
+public final class PackageImpl implements ParsingPackage, ParsedPackage, AndroidPackage,
+        AndroidPackageWrite {
+
+    private static final String TAG = "PackageImpl";
+
+    // Resource boolean are -1, so 1 means we don't know the value.
+    private int supportsSmallScreens = 1;
+    private int supportsNormalScreens = 1;
+    private int supportsLargeScreens = 1;
+    private int supportsXLargeScreens = 1;
+    private int resizeable = 1;
+    private int anyDensity = 1;
+
+    private long[] lastPackageUsageTimeInMills =
+            new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
+
+    private int versionCode;
+    private int versionCodeMajor;
+    private int baseRevisionCode;
+    private String versionName;
+
+    private boolean coreApp;
+    private int compileSdkVersion;
+    private String compileSdkVersionCodename;
+
+    private String packageName;
+    private String realPackage;
+    private String manifestPackageName;
+    private String baseCodePath;
+
+    private boolean requiredForAllUsers;
+    private String restrictedAccountType;
+    private String requiredAccountType;
+
+    private boolean baseHardwareAccelerated;
+
+    private String overlayTarget;
+    private String overlayTargetName;
+    private String overlayCategory;
+    private int overlayPriority;
+    private boolean overlayIsStatic;
+
+    private String staticSharedLibName;
+    private long staticSharedLibVersion;
+    private ArrayList<String> libraryNames;
+    private ArrayList<String> usesLibraries;
+    private ArrayList<String> usesOptionalLibraries;
+
+    private ArrayList<String> usesStaticLibraries;
+    private long[] usesStaticLibrariesVersions;
+    private String[][] usesStaticLibrariesCertDigests;
+
+    private String sharedUserId;
+
+    private int sharedUserLabel;
+    private ArrayList<ConfigurationInfo> configPreferences;
+    private ArrayList<FeatureInfo> reqFeatures;
+    private ArrayList<FeatureGroupInfo> featureGroups;
+
+    private byte[] restrictUpdateHash;
+
+    private ArrayList<String> originalPackages;
+    private ArrayList<String> adoptPermissions;
+
+    private ArrayList<String> requestedPermissions;
+    private ArrayList<String> implicitPermissions;
+
+    private ArraySet<String> upgradeKeySets;
+    private Map<String, ArraySet<PublicKey>> keySetMapping;
+
+    private ArrayList<String> protectedBroadcasts;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedActivity> activities;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedActivity> receivers;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedService> services;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedProvider> providers;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedPermission> permissions;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedPermissionGroup> permissionGroups;
+
+    @Nullable
+    private ArrayList<ComponentParseUtils.ParsedInstrumentation> instrumentations;
+
+    private ArrayList<ParsedActivityIntentInfo> preferredActivityFilters;
+
+    private Bundle appMetaData;
+
+    private String volumeUuid;
+    private String applicationVolumeUuid;
+    private PackageParser.SigningDetails signingDetails;
+
+    private String codePath;
+
+    private boolean use32BitAbi;
+    private boolean visibleToInstantApps;
+
+    private String cpuAbiOverride;
+
+    private boolean isStub;
+
+    // TODO(b/135203078): Remove, should be unused
+    private int preferredOrder;
+
+    private boolean forceQueryable;
+
+    @Nullable
+    private ArrayList<Intent> queriesIntents;
+
+    @Nullable
+    private ArrayList<String> queriesPackages;
+
+    private String[] splitClassLoaderNames;
+    private String[] splitCodePaths;
+    private SparseArray<int[]> splitDependencies;
+    private int[] splitFlags;
+    private String[] splitNames;
+    private int[] splitRevisionCodes;
+
+    // TODO(b/135203078): Audit applicationInfo.something usages, which may be different from
+    //  package.something usages. There were differing cases of package.field = versus
+    //  package.appInfo.field =. This class assumes some obvious ones, like packageName,
+    //  were collapsible, but kept the following separate.
+
+    private String applicationInfoBaseResourcePath;
+    private String applicationInfoCodePath;
+    private String applicationInfoResourcePath;
+    private String[] applicationInfoSplitResourcePaths;
+
+    private String appComponentFactory;
+    private String backupAgentName;
+    private int banner;
+    private int category;
+    private String classLoaderName;
+    private String className;
+    private int compatibleWidthLimitDp;
+    private String credentialProtectedDataDir;
+    private String dataDir;
+    private int descriptionRes;
+    private String deviceProtectedDataDir;
+    private boolean enabled;
+    private int flags;
+    private int fullBackupContent;
+    private boolean hiddenUntilInstalled;
+    private int icon;
+    private int iconRes;
+    private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
+    private int labelRes;
+    private int largestWidthLimitDp;
+    private int logo;
+    private String manageSpaceActivityName;
+    private float maxAspectRatio;
+    private float minAspectRatio;
+    private int minSdkVersion;
+    private String name;
+    private String nativeLibraryDir;
+    private String nativeLibraryRootDir;
+    private boolean nativeLibraryRootRequiresIsa;
+    private int networkSecurityConfigRes;
+    private CharSequence nonLocalizedLabel;
+    private String permission;
+    private String primaryCpuAbi;
+    private int privateFlags;
+    private String processName;
+    private int requiresSmallestWidthDp;
+    private int roundIconRes;
+    private String secondaryCpuAbi;
+    private String secondaryNativeLibraryDir;
+    private String seInfo;
+    private String seInfoUser;
+    private int targetSandboxVersion;
+    private int targetSdkVersion;
+    private String taskAffinity;
+    private int theme;
+    private int uid = -1;
+    private int uiOptions;
+    private String[] usesLibraryFiles;
+    private List<SharedLibraryInfo> usesLibraryInfos;
+    private String zygotePreloadName;
+
+    @VisibleForTesting
+    public PackageImpl(
+            String packageName,
+            String baseCodePath,
+            TypedArray manifestArray,
+            boolean isCoreApp
+    ) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        this.manifestPackageName = this.packageName;
+        this.baseCodePath = baseCodePath;
+
+        this.versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
+        this.versionCodeMajor = manifestArray.getInteger(
+                R.styleable.AndroidManifest_versionCodeMajor, 0);
+        this.baseRevisionCode = manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode,
+                0);
+        setVersionName(manifestArray.getNonConfigurationString(
+                R.styleable.AndroidManifest_versionName, 0));
+        this.coreApp = isCoreApp;
+
+        this.compileSdkVersion = manifestArray.getInteger(
+                R.styleable.AndroidManifest_compileSdkVersion, 0);
+        setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
+                R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
+    }
+
+    private PackageImpl(String packageName) {
+        this.packageName = TextUtils.safeIntern(packageName);
+        this.manifestPackageName = this.packageName;
+    }
+
+    @VisibleForTesting
+    public static ParsingPackage forParsing(String packageName) {
+        return new PackageImpl(packageName);
+    }
+
+    @VisibleForTesting
+    public static ParsingPackage forParsing(
+            String packageName,
+            String baseCodePath,
+            TypedArray manifestArray,
+            boolean isCoreApp) {
+        return new PackageImpl(packageName, baseCodePath, manifestArray, isCoreApp);
+    }
+
+    /**
+     * Mock an unavailable {@link AndroidPackage} to use when removing a package from the system.
+     * This can occur if the package was installed on a storage device that has since been removed.
+     * Since the infrastructure uses {@link AndroidPackage}, but for this case only cares about
+     * volumeUuid, just fake it rather than having separate method paths.
+     */
+    public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) {
+        return new PackageImpl(packageName)
+                .setVolumeUuid(volumeUuid)
+                .hideAsParsed()
+                .hideAsFinal();
+    }
+
+    @Override
+    public ParsedPackage hideAsParsed() {
+        return this;
+    }
+
+    @Override
+    public AndroidPackage hideAsFinal() {
+        updateFlags();
+        return this;
+    }
+
+    @Override
+    @Deprecated
+    public AndroidPackageWrite mutate() {
+        return this;
+    }
+
+    private void updateFlags() {
+        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            this.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+        }
+        if (supportsNormalScreens != 0) {
+            this.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+        }
+        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            this.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+        }
+        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.GINGERBREAD)) {
+            this.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+        }
+        if (resizeable < 0 || (resizeable > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            this.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
+        }
+        if (anyDensity < 0 || (anyDensity > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            this.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
+        }
+    }
+
+    @Override
+    public boolean usesCompatibilityMode() {
+        int flags = 0;
+
+        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+        }
+        if (supportsNormalScreens != 0) {
+            flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
+        }
+        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
+        }
+        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.GINGERBREAD)) {
+            flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
+        }
+        if (resizeable < 0 || (resizeable > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
+        }
+        if (anyDensity < 0 || (anyDensity > 0
+                && targetSdkVersion
+                >= Build.VERSION_CODES.DONUT)) {
+            flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
+        }
+
+        return targetSdkVersion < DONUT
+                || (flags & (ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
+                        | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
+                        | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
+                        | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
+                        | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
+                        | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)) == 0;
+    }
+
+    @Override
+    public String getBaseCodePath() {
+        return baseCodePath;
+    }
+
+    @Override
+    public int getTargetSdkVersion() {
+        return targetSdkVersion;
+    }
+
+    @Override
+    public String getPackageName() {
+        return packageName;
+    }
+
+    @Override
+    public String getProcessName() {
+        return processName;
+    }
+
+    @Override
+    public String getPermission() {
+        return permission;
+    }
+
+    @Override
+    public String getStaticSharedLibName() {
+        return staticSharedLibName;
+    }
+
+    @Override
+    public long getStaticSharedLibVersion() {
+        return staticSharedLibVersion;
+    }
+
+    @Override
+    public String getSharedUserId() {
+        return sharedUserId;
+    }
+
+    @Override
+    public List<String> getRequestedPermissions() {
+        return requestedPermissions == null ? Collections.emptyList() : requestedPermissions;
+    }
+
+    @Nullable
+    @Override
+    public List<ParsedInstrumentation> getInstrumentations() {
+        return instrumentations;
+    }
+
+    @Override
+    public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
+        return keySetMapping == null ? Collections.emptyMap() : keySetMapping;
+    }
+
+    @Override
+    public float getMaxAspectRatio() {
+        return maxAspectRatio;
+    }
+
+    @Override
+    public float getMinAspectRatio() {
+        return minAspectRatio;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getLibraryNames() {
+        return libraryNames == null ? Collections.emptyList() : libraryNames;
+    }
+
+    @Override
+    public List<ParsedActivity> getActivities() {
+        return activities == null ? Collections.emptyList()
+                : activities;
+    }
+
+    @Override
+    public Bundle getAppMetaData() {
+        return appMetaData;
+    }
+
+    @Nullable
+    @Override
+    public List<String> getUsesLibraries() {
+        return usesLibraries;
+    }
+
+    @Nullable
+    @Override
+    public List<String> getUsesStaticLibraries() {
+        return usesStaticLibraries;
+    }
+
+    @Override
+    public boolean isBaseHardwareAccelerated() {
+        return baseHardwareAccelerated;
+    }
+
+    @Override
+    public int getUiOptions() {
+        return uiOptions;
+    }
+
+    // TODO(b/135203078): Checking flags directly can be error prone,
+    //  consider separate interface methods?
+    @Override
+    public int getFlags() {
+        return flags;
+    }
+
+    // TODO(b/135203078): Checking flags directly can be error prone,
+    //  consider separate interface methods?
+    @Override
+    public int getPrivateFlags() {
+        return privateFlags;
+    }
+
+    @Override
+    public String getTaskAffinity() {
+        return taskAffinity;
+    }
+
+    @Nullable
+    @Override
+    public List<String> getOriginalPackages() {
+        return originalPackages;
+    }
+
+    @Override
+    public PackageParser.SigningDetails getSigningDetails() {
+        return signingDetails;
+    }
+
+    @Override
+    public String getVolumeUuid() {
+        return volumeUuid;
+    }
+
+    @Nullable
+    @Override
+    public List<ParsedPermissionGroup> getPermissionGroups() {
+        return permissionGroups;
+    }
+
+    @Nullable
+    @Override
+    public List<ParsedPermission> getPermissions() {
+        return permissions;
+    }
+
+    @Override
+    public String getCpuAbiOverride() {
+        return cpuAbiOverride;
+    }
+
+    @Override
+    public String getPrimaryCpuAbi() {
+        return primaryCpuAbi;
+    }
+
+    @Override
+    public String getSecondaryCpuAbi() {
+        return secondaryCpuAbi;
+    }
+
+    @Override
+    public boolean isUse32BitAbi() {
+        return use32BitAbi;
+    }
+
+    @Override
+    public boolean isForceQueryable() {
+        return forceQueryable;
+    }
+
+    @Override
+    public String getCodePath() {
+        return codePath;
+    }
+
+    @Override
+    public String getNativeLibraryDir() {
+        return nativeLibraryDir;
+    }
+
+    @Override
+    public String getNativeLibraryRootDir() {
+        return nativeLibraryRootDir;
+    }
+
+    @Override
+    public boolean isNativeLibraryRootRequiresIsa() {
+        return nativeLibraryRootRequiresIsa;
+    }
+
+    // TODO(b/135203078): Does nothing, remove?
+    @Override
+    public int getPreferredOrder() {
+        return preferredOrder;
+    }
+
+    @Override
+    public long getLongVersionCode() {
+        return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    @Override
+    public PackageImpl setIsOverlay(boolean isOverlay) {
+        this.privateFlags = isOverlay
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setExternalStorage(boolean externalStorage) {
+        this.flags = externalStorage
+                ? this.flags | ApplicationInfo.FLAG_EXTERNAL_STORAGE
+                : this.flags & ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setIsolatedSplitLoading(boolean isolatedSplitLoading) {
+        this.privateFlags = isolatedSplitLoading
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        return this;
+    }
+
+    @Override
+    public PackageImpl sortActivities() {
+        Collections.sort(this.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+        return this;
+    }
+
+    @Override
+    public PackageImpl sortReceivers() {
+        Collections.sort(this.receivers, (a1, a2) -> Integer.compare(a2.order, a1.order));
+        return this;
+    }
+
+    @Override
+    public PackageImpl sortServices() {
+        Collections.sort(this.services, (a1, a2) -> Integer.compare(a2.order, a1.order));
+        return this;
+    }
+
+    @Override
+    public PackageImpl setBaseRevisionCode(int baseRevisionCode) {
+        this.baseRevisionCode = baseRevisionCode;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setPreferredOrder(int preferredOrder) {
+        this.preferredOrder = preferredOrder;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setVersionName(String versionName) {
+        this.versionName = TextUtils.safeIntern(versionName);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setCompileSdkVersion(int compileSdkVersion) {
+        this.compileSdkVersion = compileSdkVersion;
+        return this;
+    }
+
+    @Override
+    public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) {
+        this.compileSdkVersionCodename = TextUtils.safeIntern(compileSdkVersionCodename);
+        return this;
+    }
+
+    @Override
+    public PackageImpl setMaxAspectRatio(float maxAspectRatio) {
+        this.maxAspectRatio = maxAspectRatio;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setMinAspectRatio(float minAspectRatio) {
+        this.minAspectRatio = minAspectRatio;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setMinSdkVersion(int minSdkVersion) {
+        this.minSdkVersion = minSdkVersion;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setTargetSdkVersion(int targetSdkVersion) {
+        this.targetSdkVersion = targetSdkVersion;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRealPackage(String realPackage) {
+        this.realPackage = realPackage;
+        return this;
+    }
+
+    @Override
+    public PackageImpl addConfigPreference(ConfigurationInfo configPreference) {
+        this.configPreferences = ArrayUtils.add(this.configPreferences, configPreference);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addReqFeature(FeatureInfo reqFeature) {
+        this.reqFeatures = ArrayUtils.add(this.reqFeatures, reqFeature);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
+        this.featureGroups = ArrayUtils.add(this.featureGroups, featureGroup);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addProtectedBroadcast(String protectedBroadcast) {
+        if (this.protectedBroadcasts == null
+                || !this.protectedBroadcasts.contains(protectedBroadcast)) {
+            this.protectedBroadcasts = ArrayUtils.add(this.protectedBroadcasts,
+                    TextUtils.safeIntern(protectedBroadcast));
+        }
+        return this;
+    }
+
+    @Override
+    public PackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
+        this.instrumentations = ArrayUtils.add(this.instrumentations, instrumentation);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addOriginalPackage(String originalPackage) {
+        this.originalPackages = ArrayUtils.add(this.originalPackages, originalPackage);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addAdoptPermission(String adoptPermission) {
+        this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addPermission(ParsedPermission permission) {
+        this.permissions = ArrayUtils.add(this.permissions, permission);
+        return this;
+    }
+
+    @Override
+    public PackageImpl removePermission(int index) {
+        this.permissions.remove(index);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
+        this.permissionGroups = ArrayUtils.add(this.permissionGroups, permissionGroup);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addRequestedPermission(String permission) {
+        this.requestedPermissions = ArrayUtils.add(this.requestedPermissions,
+                TextUtils.safeIntern(permission));
+        return this;
+    }
+
+    @Override
+    public PackageImpl addImplicitPermission(String permission) {
+        this.implicitPermissions = ArrayUtils.add(this.implicitPermissions,
+                TextUtils.safeIntern(permission));
+        return this;
+    }
+
+    @Override
+    public PackageImpl addKeySet(String keySetName, PublicKey publicKey) {
+        if (keySetMapping == null) {
+            keySetMapping = new ArrayMap<>();
+        }
+
+        ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
+        if (publicKeys == null) {
+            publicKeys = new ArraySet<>();
+            keySetMapping.put(keySetName, publicKeys);
+        }
+
+        publicKeys.add(publicKey);
+
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addActivity(ParsedActivity parsedActivity) {
+        this.activities = ArrayUtils.add(this.activities, parsedActivity);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addReceiver(ParsedActivity parsedReceiver) {
+        this.receivers = ArrayUtils.add(this.receivers, parsedReceiver);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addService(ParsedService parsedService) {
+        this.services = ArrayUtils.add(this.services, parsedService);
+        return this;
+    }
+
+    @Override
+    public ParsingPackage addProvider(ParsedProvider parsedProvider) {
+        this.providers = ArrayUtils.add(this.providers, parsedProvider);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addLibraryName(String libraryName) {
+        this.libraryNames = ArrayUtils.add(this.libraryNames, TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesLibrary(String libraryName) {
+        this.usesLibraries = ArrayUtils.add(this.usesLibraries, TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = ArrayUtils.add(this.usesOptionalLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public PackageImpl removeUsesOptionalLibrary(String libraryName) {
+        this.usesOptionalLibraries = ArrayUtils.remove(this.usesOptionalLibraries, libraryName);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesStaticLibrary(String libraryName) {
+        this.usesStaticLibraries = ArrayUtils.add(this.usesStaticLibraries,
+                TextUtils.safeIntern(libraryName));
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesStaticLibraryVersion(long version) {
+        this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
+                version, true);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
+        this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+                this.usesStaticLibrariesCertDigests, certSha256Digests, true);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addPreferredActivityFilter(
+            ParsedActivityIntentInfo parsedActivityIntentInfo) {
+        this.preferredActivityFilters = ArrayUtils.add(this.preferredActivityFilters,
+                parsedActivityIntentInfo);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addQueriesIntent(Intent intent) {
+        this.queriesIntents = ArrayUtils.add(this.queriesIntents, intent);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addQueriesPackage(String packageName) {
+        this.queriesPackages = ArrayUtils.add(this.queriesPackages,
+                TextUtils.safeIntern(packageName));
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
+        if (supportsSmallScreens == 1) {
+            return this;
+        }
+
+        this.supportsSmallScreens = supportsSmallScreens;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
+        if (supportsNormalScreens == 1) {
+            return this;
+        }
+
+        this.supportsNormalScreens = supportsNormalScreens;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
+        if (supportsLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsLargeScreens = supportsLargeScreens;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSupportsXLargeScreens(int supportsXLargeScreens) {
+        if (supportsXLargeScreens == 1) {
+            return this;
+        }
+
+        this.supportsXLargeScreens = supportsXLargeScreens;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setResizeable(int resizeable) {
+        if (resizeable == 1) {
+            return this;
+        }
+
+        this.resizeable = resizeable;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAnyDensity(int anyDensity) {
+        if (anyDensity == 1) {
+            return this;
+        }
+
+        this.anyDensity = anyDensity;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRequiresSmallestWidthDp(int requiresSmallestWidthDp) {
+        this.requiresSmallestWidthDp = requiresSmallestWidthDp;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCompatibleWidthLimitDp(int compatibleWidthLimitDp) {
+        this.compatibleWidthLimitDp = compatibleWidthLimitDp;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setLargestWidthLimitDp(int largestWidthLimitDp) {
+        this.largestWidthLimitDp = largestWidthLimitDp;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setInstallLocation(int installLocation) {
+        this.installLocation = installLocation;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setTargetSandboxVersion(int targetSandboxVersion) {
+        this.targetSandboxVersion = targetSandboxVersion;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRequiredForAllUsers(boolean requiredForAllUsers) {
+        this.requiredForAllUsers = requiredForAllUsers;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRestrictedAccountType(String restrictedAccountType) {
+        this.restrictedAccountType = restrictedAccountType;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRequiredAccountType(String requiredAccountType) {
+        this.requiredAccountType = requiredAccountType;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setBaseHardwareAccelerated(boolean baseHardwareAccelerated) {
+        this.baseHardwareAccelerated = baseHardwareAccelerated;
+
+        this.flags = baseHardwareAccelerated
+                ? this.flags | ApplicationInfo.FLAG_HARDWARE_ACCELERATED
+                : this.flags & ~ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
+
+        return this;
+    }
+
+    @Override
+    public PackageImpl setHasDomainUrls(boolean hasDomainUrls) {
+        this.privateFlags = hasDomainUrls
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAppMetaData(Bundle appMetaData) {
+        this.appMetaData = appMetaData;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setOverlayTarget(String overlayTarget) {
+        this.overlayTarget = overlayTarget;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setOverlayTargetName(String overlayTargetName) {
+        this.overlayTargetName = overlayTargetName;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setOverlayCategory(String overlayCategory) {
+        this.overlayCategory = overlayCategory;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setOverlayPriority(int overlayPriority) {
+        this.overlayPriority = overlayPriority;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setOverlayIsStatic(boolean overlayIsStatic) {
+        this.overlayIsStatic = overlayIsStatic;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setStaticSharedLibName(String staticSharedLibName) {
+        this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
+        return this;
+    }
+
+    @Override
+    public PackageImpl setStaticSharedLibVersion(long staticSharedLibVersion) {
+        this.staticSharedLibVersion = staticSharedLibVersion;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSharedUserId(String sharedUserId) {
+        this.sharedUserId = TextUtils.safeIntern(sharedUserId);
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSharedUserLabel(int sharedUserLabel) {
+        this.sharedUserLabel = sharedUserLabel;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRestrictUpdateHash(byte[] restrictUpdateHash) {
+        this.restrictUpdateHash = restrictUpdateHash;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUpgradeKeySets(ArraySet<String> upgradeKeySets) {
+        this.upgradeKeySets = upgradeKeySets;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setVolumeUuid(String volumeUuid) {
+        this.volumeUuid = volumeUuid;
+        return this;
+    }
+
+    @Deprecated
+    @Override
+    public PackageImpl setApplicationVolumeUuid(String applicationVolumeUuid) {
+        this.applicationVolumeUuid = applicationVolumeUuid;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSigningDetails(PackageParser.SigningDetails signingDetails) {
+        this.signingDetails = signingDetails;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCodePath(String codePath) {
+        this.codePath = codePath;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUse32BitAbi(boolean use32BitAbi) {
+        this.use32BitAbi = use32BitAbi;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCpuAbiOverride(String cpuAbiOverride) {
+        this.cpuAbiOverride = cpuAbiOverride;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setForceQueryable(boolean forceQueryable) {
+        this.forceQueryable = forceQueryable;
+        return this;
+    }
+
+    // TODO(b/135203078): Remove and move PackageManagerService#renameStaticSharedLibraryPackage
+    //  into initial package parsing
+    @Override
+    public PackageImpl setPackageName(String packageName) {
+        this.packageName = packageName.intern();
+
+        if (permissions != null) {
+            for (ParsedPermission permission : permissions) {
+                permission.setPackageName(this.packageName);
+            }
+        }
+
+        if (permissionGroups != null) {
+            for (ParsedPermissionGroup permissionGroup : permissionGroups) {
+                permissionGroup.setPackageName(this.packageName);
+            }
+        }
+
+        if (activities != null) {
+            for (ParsedActivity parsedActivity : activities) {
+                parsedActivity.setPackageName(this.packageName);
+            }
+        }
+
+        if (receivers != null) {
+            for (ParsedActivity receiver : receivers) {
+                receiver.setPackageName(this.packageName);
+            }
+        }
+
+        if (providers != null) {
+            for (ParsedProvider provider : providers) {
+                provider.setPackageName(this.packageName);
+            }
+        }
+
+        if (services != null) {
+            for (ParsedService service : services) {
+                service.setPackageName(this.packageName);
+            }
+        }
+
+        if (instrumentations != null) {
+            for (ParsedInstrumentation instrumentation : instrumentations) {
+                instrumentation.setPackageName(this.packageName);
+            }
+        }
+
+        return this;
+    }
+
+    // Under this is parseBaseApplication
+
+    @Override
+    public PackageImpl setAllowBackup(boolean allowBackup) {
+        this.flags = allowBackup
+                ? this.flags | ApplicationInfo.FLAG_ALLOW_BACKUP
+                : this.flags & ~ApplicationInfo.FLAG_ALLOW_BACKUP;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setKillAfterRestore(boolean killAfterRestore) {
+        this.flags = killAfterRestore
+                ? this.flags | ApplicationInfo.FLAG_KILL_AFTER_RESTORE
+                : this.flags & ~ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRestoreAnyVersion(boolean restoreAnyVersion) {
+        this.flags = restoreAnyVersion
+                ? this.flags | ApplicationInfo.FLAG_RESTORE_ANY_VERSION
+                : this.flags & ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setFullBackupOnly(boolean fullBackupOnly) {
+        this.flags = fullBackupOnly
+                ? this.flags | ApplicationInfo.FLAG_FULL_BACKUP_ONLY
+                : this.flags & ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setPersistent(boolean persistent) {
+        this.flags = persistent
+                ? this.flags | ApplicationInfo.FLAG_PERSISTENT
+                : this.flags & ~ApplicationInfo.FLAG_PERSISTENT;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setDebuggable(boolean debuggable) {
+        this.flags = debuggable
+                ? this.flags | ApplicationInfo.FLAG_DEBUGGABLE
+                : this.flags & ~ApplicationInfo.FLAG_DEBUGGABLE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setProfileableByShell(boolean profileableByShell) {
+        this.privateFlags = profileableByShell
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setVmSafeMode(boolean vmSafeMode) {
+        this.flags = vmSafeMode
+                ? this.flags | ApplicationInfo.FLAG_VM_SAFE_MODE
+                : this.flags & ~ApplicationInfo.FLAG_VM_SAFE_MODE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setHasCode(boolean hasCode) {
+        this.flags = hasCode
+                ? this.flags | ApplicationInfo.FLAG_HAS_CODE
+                : this.flags & ~ApplicationInfo.FLAG_HAS_CODE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAllowTaskReparenting(boolean allowTaskReparenting) {
+        this.flags = allowTaskReparenting
+                ? this.flags | ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING
+                : this.flags & ~ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAllowClearUserData(boolean allowClearUserData) {
+        this.flags = allowClearUserData
+                ? this.flags | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA
+                : this.flags & ~ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setLargeHeap(boolean largeHeap) {
+        this.flags = largeHeap
+                ? this.flags | ApplicationInfo.FLAG_LARGE_HEAP
+                : this.flags & ~ApplicationInfo.FLAG_LARGE_HEAP;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUsesCleartextTraffic(boolean usesCleartextTraffic) {
+        this.flags = usesCleartextTraffic
+                ? this.flags | ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC
+                : this.flags & ~ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSupportsRtl(boolean supportsRtl) {
+        this.flags = supportsRtl
+                ? this.flags | ApplicationInfo.FLAG_SUPPORTS_RTL
+                : this.flags & ~ApplicationInfo.FLAG_SUPPORTS_RTL;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setTestOnly(boolean testOnly) {
+        this.flags = testOnly
+                ? this.flags | ApplicationInfo.FLAG_TEST_ONLY
+                : this.flags & ~ApplicationInfo.FLAG_TEST_ONLY;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setMultiArch(boolean multiArch) {
+        this.flags = multiArch
+                ? this.flags | ApplicationInfo.FLAG_MULTIARCH
+                : this.flags & ~ApplicationInfo.FLAG_MULTIARCH;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setExtractNativeLibs(boolean extractNativeLibs) {
+        this.flags = extractNativeLibs
+                ? this.flags | ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS
+                : this.flags & ~ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setIsGame(boolean isGame) {
+        this.flags = isGame
+                ? this.flags | ApplicationInfo.FLAG_IS_GAME
+                : this.flags & ~ApplicationInfo.FLAG_IS_GAME;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setBackupInForeground(boolean backupInForeground) {
+        this.privateFlags = backupInForeground
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUseEmbeddedDex(boolean useEmbeddedDex) {
+        this.privateFlags = useEmbeddedDex
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage) {
+        this.privateFlags = defaultToDeviceProtectedStorage
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setDirectBootAware(boolean directBootAware) {
+        this.privateFlags = directBootAware
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setPartiallyDirectBootAware(boolean partiallyDirectBootAware) {
+        this.privateFlags = partiallyDirectBootAware
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setActivitiesResizeModeResizeableViaSdkVersion(
+            boolean resizeableViaSdkVersion
+    ) {
+        this.privateFlags = resizeableViaSdkVersion
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setActivitiesResizeModeResizeable(boolean resizeable) {
+        this.privateFlags = resizeable
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+
+        this.privateFlags = !resizeable
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAllowClearUserDataOnFailedRestore(
+            boolean allowClearUserDataOnFailedRestore
+    ) {
+        this.privateFlags = allowClearUserDataOnFailedRestore
+                ? this.privateFlags | ApplicationInfo
+                        .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
+                : this.privateFlags & ~ApplicationInfo
+                        .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture) {
+        this.privateFlags = allowAudioPlaybackCapture
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage) {
+        this.privateFlags = requestLegacyExternalStorage
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
+        this.privateFlags = usesNonSdkApi
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setHasFragileUserData(boolean hasFragileUserData) {
+        this.privateFlags = hasFragileUserData
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCantSaveState(boolean cantSaveState) {
+        this.privateFlags = cantSaveState
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+        return this;
+    }
+
+    @Override
+    public boolean cantSaveState() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0;
+    }
+
+    @Override
+    public boolean isLibrary() {
+        return staticSharedLibName != null || !ArrayUtils.isEmpty(libraryNames);
+    }
+
+    // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
+    //  part of PackageParser
+    @Override
+    public boolean isSystemApp() {
+        return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
+    //  part of PackageParser
+    @Override
+    public boolean isSystemExt() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
+    }
+
+    // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
+    //  part of PackageParser
+    @Override
+    public boolean isUpdatedSystemApp() {
+        return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+    }
+
+    @Override
+    public PackageImpl setStaticSharedLibrary(boolean staticSharedLibrary) {
+        this.privateFlags = staticSharedLibrary
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
+        return this;
+    }
+
+    @Override
+    public boolean isStaticSharedLibrary() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
+    }
+
+    @Override
+    public PackageImpl setVisibleToInstantApps(boolean visibleToInstantApps) {
+        this.visibleToInstantApps = visibleToInstantApps;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setIconRes(int iconRes) {
+        this.iconRes = iconRes;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setRoundIconRes(int roundIconRes) {
+        this.roundIconRes = roundIconRes;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setClassName(String className) {
+        this.className = className;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setManageSpaceActivityName(String manageSpaceActivityName) {
+        this.manageSpaceActivityName = manageSpaceActivityName;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setBackupAgentName(String backupAgentName) {
+        this.backupAgentName = backupAgentName;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setFullBackupContent(int fullBackupContent) {
+        this.fullBackupContent = fullBackupContent;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setTheme(int theme) {
+        this.theme = theme;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setDescriptionRes(int descriptionRes) {
+        this.descriptionRes = descriptionRes;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setNetworkSecurityConfigRes(int networkSecurityConfigRes) {
+        this.networkSecurityConfigRes = networkSecurityConfigRes;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCategory(int category) {
+        this.category = category;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setPermission(String permission) {
+        this.permission = permission;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setTaskAffinity(String taskAffinity) {
+        this.taskAffinity = taskAffinity;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setAppComponentFactory(String appComponentFactory) {
+        this.appComponentFactory = appComponentFactory;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setProcessName(String processName) {
+        if (processName == null) {
+            this.processName = packageName;
+        } else {
+            this.processName = processName;
+        }
+        return this;
+    }
+
+    @Override
+    public PackageImpl setEnabled(boolean enabled) {
+        this.enabled = enabled;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUiOptions(int uiOptions) {
+        this.uiOptions = uiOptions;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setClassLoaderName(String classLoaderName) {
+        this.classLoaderName = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setZygotePreloadName(String zygotePreloadName) {
+        this.zygotePreloadName = zygotePreloadName;
+        return this;
+    }
+
+    // parsePackageItemInfo
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public PackageImpl setName(String name) {
+        this.name = name;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setIcon(int icon) {
+        this.icon = icon;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
+        this.nonLocalizedLabel = nonLocalizedLabel;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setLogo(int logo) {
+        this.logo = logo;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setBanner(int banner) {
+        this.banner = banner;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setLabelRes(int labelRes) {
+        this.labelRes = labelRes;
+        return this;
+    }
+
+    @Override
+    public PackageImpl asSplit(
+            String[] splitNames,
+            String[] splitCodePaths,
+            int[] splitRevisionCodes,
+            SparseArray<int[]> splitDependencies
+    ) {
+        this.splitNames = splitNames;
+
+        if (this.splitNames != null) {
+            for (int index = 0; index < this.splitNames.length; index++) {
+                splitNames[index] = TextUtils.safeIntern(splitNames[index]);
+            }
+        }
+
+        this.splitCodePaths = splitCodePaths;
+        this.splitRevisionCodes = splitRevisionCodes;
+        this.splitDependencies = splitDependencies;
+
+        int count = splitNames.length;
+        this.splitFlags = new int[count];
+        this.splitClassLoaderNames = new String[count];
+        return this;
+    }
+
+    @Override
+    public String[] getSplitNames() {
+        return splitNames;
+    }
+
+    @Override
+    public String[] getSplitCodePaths() {
+        return splitCodePaths;
+    }
+
+    @Override
+    public PackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
+        this.splitFlags[splitIndex] = splitHasCode
+                ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
+                : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
+        this.splitClassLoaderNames[splitIndex] = classLoaderName;
+        return this;
+    }
+
+    @Override
+    public List<String> makeListAllCodePaths() {
+        ArrayList<String> paths = new ArrayList<>();
+        paths.add(baseCodePath);
+
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            Collections.addAll(paths, splitCodePaths);
+        }
+        return paths;
+    }
+
+    @Override
+    public PackageImpl setBaseCodePath(String baseCodePath) {
+        this.baseCodePath = baseCodePath;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSplitCodePaths(String[] splitCodePaths) {
+        this.splitCodePaths = splitCodePaths;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return "Package{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " " + packageName + "}";
+    }
+
+    @Override
+    public PackageImpl setPrimaryCpuAbi(String primaryCpuAbi) {
+        this.primaryCpuAbi = primaryCpuAbi;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSecondaryCpuAbi(String secondaryCpuAbi) {
+        this.secondaryCpuAbi = secondaryCpuAbi;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setNativeLibraryRootDir(String nativeLibraryRootDir) {
+        this.nativeLibraryRootDir = nativeLibraryRootDir;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa) {
+        this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setNativeLibraryDir(String nativeLibraryDir) {
+        this.nativeLibraryDir = nativeLibraryDir;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir) {
+        this.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+        return this;
+    }
+
+    @Deprecated
+    @Override
+    public PackageImpl setApplicationInfoCodePath(String applicationInfoCodePath) {
+        this.applicationInfoCodePath = applicationInfoCodePath;
+        return this;
+    }
+
+    @Deprecated
+    @Override
+    public PackageImpl setApplicationInfoResourcePath(String applicationInfoResourcePath) {
+        this.applicationInfoResourcePath = applicationInfoResourcePath;
+        return this;
+    }
+
+    @Deprecated
+    @Override
+    public PackageImpl setApplicationInfoBaseResourcePath(
+            String applicationInfoBaseResourcePath) {
+        this.applicationInfoBaseResourcePath = applicationInfoBaseResourcePath;
+        return this;
+    }
+
+    @Deprecated
+    @Override
+    public PackageImpl setApplicationInfoSplitResourcePaths(
+            String[] applicationInfoSplitResourcePaths) {
+        this.applicationInfoSplitResourcePaths = applicationInfoSplitResourcePaths;
+        return this;
+    }
+
+    @Override
+    public boolean isDirectBootAware() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
+    }
+
+    @Override
+    public PackageImpl setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware) {
+        if (activities != null) {
+            for (ParsedActivity parsedActivity : activities) {
+                parsedActivity.directBootAware = allComponentsDirectBootAware;
+            }
+        }
+
+        if (receivers != null) {
+            for (ParsedActivity parsedReceiver : receivers) {
+                parsedReceiver.directBootAware = allComponentsDirectBootAware;
+            }
+        }
+
+        if (providers != null) {
+            for (ParsedProvider parsedProvider : providers) {
+                parsedProvider.directBootAware = allComponentsDirectBootAware;
+            }
+        }
+
+        if (services != null) {
+            for (ParsedService parsedService : services) {
+                parsedService.directBootAware = allComponentsDirectBootAware;
+            }
+        }
+
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSystem(boolean system) {
+        this.flags = system
+                ? this.flags | ApplicationInfo.FLAG_SYSTEM
+                : this.flags & ~ApplicationInfo.FLAG_SYSTEM;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSystemExt(boolean systemExt) {
+        this.privateFlags = systemExt
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setIsStub(boolean isStub) {
+        this.isStub = isStub;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setCoreApp(boolean coreApp) {
+        this.coreApp = coreApp;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage capPermissionPriorities() {
+        if (permissionGroups != null && !permissionGroups.isEmpty()) {
+            for (int i = permissionGroups.size() - 1; i >= 0; --i) {
+                // TODO(b/135203078): Builder/immutability
+                permissionGroups.get(i).priority = 0;
+            }
+        }
+        return this;
+    }
+
+    @Override
+    public ParsedPackage clearProtectedBroadcasts() {
+        if (protectedBroadcasts != null) {
+            protectedBroadcasts.clear();
+        }
+        return this;
+    }
+
+    @Override
+    public ParsedPackage markNotActivitiesAsNotExportedIfSingleUser() {
+        // ignore export request for single user receivers
+        if (receivers != null) {
+            for (ParsedActivity receiver : receivers) {
+                if ((receiver.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+                    receiver.exported = false;
+                }
+            }
+        }
+        // ignore export request for single user services
+        if (services != null) {
+            for (ParsedService service : services) {
+                if ((service.flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
+                    service.exported = false;
+                }
+            }
+        }
+        // ignore export request for single user providers
+        if (providers != null) {
+            for (ParsedProvider provider : providers) {
+                if ((provider.flags & ProviderInfo.FLAG_SINGLE_USER) != 0) {
+                    provider.exported = false;
+                }
+            }
+        }
+
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setPrivileged(boolean privileged) {
+        this.privateFlags = privileged
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setOem(boolean oem) {
+        this.privateFlags = oem
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_OEM
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_OEM;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setVendor(boolean vendor) {
+        this.privateFlags = vendor
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_VENDOR
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_VENDOR;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setProduct(boolean product) {
+        this.privateFlags = product
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRODUCT
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRODUCT;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setOdm(boolean odm) {
+        this.privateFlags = odm
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ODM
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ODM;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setSignedWithPlatformKey(boolean signedWithPlatformKey) {
+        this.privateFlags = signedWithPlatformKey
+                ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
+                : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
+        return this;
+    }
+
+    @Override
+    public ParsedPackage clearOriginalPackages() {
+        if (originalPackages != null) {
+            originalPackages.clear();
+        }
+        return this;
+    }
+
+    @Override
+    public ParsedPackage clearAdoptPermissions() {
+        if (adoptPermissions != null) {
+            adoptPermissions.clear();
+        }
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesLibrary(int index, String libraryName) {
+        this.usesLibraries = ArrayUtils.add(usesLibraries, index, libraryName);
+        return this;
+    }
+
+    @Override
+    public ParsedPackage removeUsesLibrary(String libraryName) {
+        this.usesLibraries = ArrayUtils.remove(this.usesLibraries, libraryName);
+        return this;
+    }
+
+    @Override
+    public PackageImpl addUsesOptionalLibrary(int index, String libraryName) {
+        this.usesOptionalLibraries = ArrayUtils.add(usesOptionalLibraries, index, libraryName);
+        return this;
+    }
+
+    @Nullable
+    @Override
+    public List<String> getUsesOptionalLibraries() {
+        return usesOptionalLibraries;
+    }
+
+    @Override
+    public int getVersionCode() {
+        return versionCode;
+    }
+
+    @Nullable
+    @Override
+    public long[] getUsesStaticLibrariesVersions() {
+        return usesStaticLibrariesVersions;
+    }
+
+    @Override
+    public PackageImpl setPackageSettingCallback(PackageSettingCallback packageSettingCallback) {
+        packageSettingCallback.setAndroidPackage(this);
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUpdatedSystemApp(boolean updatedSystemApp) {
+        this.flags = updatedSystemApp
+                ? this.flags | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
+                : this.flags & ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        return this;
+    }
+
+    @Override
+    public boolean isPrivileged() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+    }
+
+    @Override
+    public PackageImpl setSeInfo(String seInfo) {
+        this.seInfo = seInfo;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setSeInfoUser(String seInfoUser) {
+        this.seInfoUser = seInfoUser;
+        return this;
+    }
+
+    @Override
+    public PackageImpl initForUser(int userId) {
+        // TODO(b/135203078): Move this user state to some other data structure
+        this.uid = UserHandle.getUid(userId, UserHandle.getAppId(this.uid));
+
+        if ("android".equals(packageName)) {
+            dataDir = Environment.getDataSystemDirectory().getAbsolutePath();
+            return this;
+        }
+
+        deviceProtectedDataDir = Environment
+                .getDataUserDePackageDirectory(applicationVolumeUuid, userId, packageName)
+                .getAbsolutePath();
+        credentialProtectedDataDir = Environment
+                .getDataUserCePackageDirectory(applicationVolumeUuid, userId, packageName)
+                .getAbsolutePath();
+
+        if ((privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0
+                && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+            dataDir = deviceProtectedDataDir;
+        } else {
+            dataDir = credentialProtectedDataDir;
+        }
+        return this;
+    }
+
+    @Override
+    public ParsedPackage setFactoryTest(boolean factoryTest) {
+        this.flags = factoryTest
+                ? this.flags | ApplicationInfo.FLAG_FACTORY_TEST
+                : this.flags & ~ApplicationInfo.FLAG_FACTORY_TEST;
+        return this;
+    }
+
+    @Override
+    public String getManifestPackageName() {
+        return manifestPackageName;
+    }
+
+    @Override
+    public String getRealPackage() {
+        return realPackage;
+    }
+
+    @Override
+    public String getOverlayTarget() {
+        return overlayTarget;
+    }
+
+    @Override
+    public String getOverlayTargetName() {
+        return overlayTargetName;
+    }
+
+    @Override
+    public boolean isOverlayIsStatic() {
+        return overlayIsStatic;
+    }
+
+    @Override
+    public int[] getSplitFlags() {
+        return splitFlags;
+    }
+
+    @Deprecated
+    @Override
+    public String getApplicationInfoVolumeUuid() {
+        return applicationVolumeUuid;
+    }
+
+    @Nullable
+    @Override
+    public List<String> getProtectedBroadcasts() {
+        return protectedBroadcasts;
+    }
+
+    @Nullable
+    @Override
+    public Set<String> getUpgradeKeySets() {
+        return upgradeKeySets;
+    }
+
+    @Nullable
+    @Override
+    public String[][] getUsesStaticLibrariesCertDigests() {
+        return usesStaticLibrariesCertDigests;
+    }
+
+    @Override
+    public int getOverlayPriority() {
+        return overlayPriority;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoPackageName() {
+        return packageName;
+    }
+
+    @Override
+    public UUID getStorageUuid() {
+        return StorageManager.convert(applicationVolumeUuid);
+    }
+
+    @Override
+    public int getUid() {
+        return uid;
+    }
+
+    @Override
+    public boolean isStub() {
+        return isStub;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoCodePath() {
+        return applicationInfoCodePath;
+    }
+
+    @Override
+    public boolean isSystem() {
+        return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    @Override
+    public boolean isMatch(int flags) {
+        if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+            return isSystem();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isVisibleToInstantApps() {
+        return visibleToInstantApps;
+    }
+
+    @Override
+    public PackageImpl setLastPackageUsageTimeInMills(int reason, long time) {
+        lastPackageUsageTimeInMills[reason] = time;
+        return this;
+    }
+
+    @Override
+    public List<SharedLibraryInfo> getUsesLibraryInfos() {
+        return usesLibraryInfos;
+    }
+
+    @NonNull
+    @Override
+    public List<String> getAllCodePaths() {
+        return makeListAllCodePaths();
+    }
+
+    @Nullable
+    @Override
+    public String[] getUsesLibraryFiles() {
+        return usesLibraryFiles;
+    }
+
+    @Override
+    public PackageImpl setUsesLibraryInfos(
+            @Nullable List<SharedLibraryInfo> usesLibraryInfos) {
+        this.usesLibraryInfos = usesLibraryInfos;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUsesLibraryFiles(@Nullable String[] usesLibraryFiles) {
+        this.usesLibraryFiles = usesLibraryFiles;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setUid(int uid) {
+        this.uid = uid;
+        return this;
+    }
+
+    @Override
+    public List<String> getAdoptPermissions() {
+        return adoptPermissions;
+    }
+
+    @Override
+    public ApplicationInfo toAppInfo() {
+        updateFlags();
+
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.packageName = packageName;
+        applicationInfo.flags = flags;
+        applicationInfo.privateFlags = privateFlags;
+        applicationInfo.sharedLibraryFiles = usesLibraryFiles;
+        applicationInfo.sharedLibraryInfos = usesLibraryInfos;
+
+        applicationInfo.appComponentFactory = appComponentFactory;
+        applicationInfo.backupAgentName = backupAgentName;
+        applicationInfo.banner = banner;
+        applicationInfo.category = category;
+        applicationInfo.classLoaderName = classLoaderName;
+        applicationInfo.className = className;
+        applicationInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
+        applicationInfo.credentialProtectedDataDir = credentialProtectedDataDir;
+        applicationInfo.dataDir = dataDir;
+        applicationInfo.descriptionRes = descriptionRes;
+        applicationInfo.deviceProtectedDataDir = deviceProtectedDataDir;
+        applicationInfo.enabled = enabled;
+        applicationInfo.fullBackupContent = fullBackupContent;
+        applicationInfo.icon = icon;
+        applicationInfo.iconRes = iconRes;
+        applicationInfo.installLocation = installLocation;
+        applicationInfo.labelRes = labelRes;
+        applicationInfo.largestWidthLimitDp = largestWidthLimitDp;
+        applicationInfo.logo = logo;
+        applicationInfo.manageSpaceActivityName = manageSpaceActivityName;
+        applicationInfo.maxAspectRatio = maxAspectRatio;
+        applicationInfo.minAspectRatio = minAspectRatio;
+        applicationInfo.minSdkVersion = minSdkVersion;
+        applicationInfo.name = name;
+        applicationInfo.nativeLibraryDir = nativeLibraryDir;
+        applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+        applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+        applicationInfo.networkSecurityConfigRes = networkSecurityConfigRes;
+        applicationInfo.nonLocalizedLabel = nonLocalizedLabel;
+        applicationInfo.permission = permission;
+        applicationInfo.primaryCpuAbi = primaryCpuAbi;
+        applicationInfo.processName = processName;
+        applicationInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
+        applicationInfo.roundIconRes = roundIconRes;
+        applicationInfo.secondaryCpuAbi = secondaryCpuAbi;
+        applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+        applicationInfo.seInfo = seInfo;
+        applicationInfo.seInfoUser = seInfoUser;
+        applicationInfo.splitClassLoaderNames = splitClassLoaderNames;
+        applicationInfo.splitDependencies = splitDependencies;
+        applicationInfo.splitNames = splitNames;
+        applicationInfo.storageUuid = StorageManager.convert(applicationVolumeUuid);
+        applicationInfo.targetSandboxVersion = targetSandboxVersion;
+        applicationInfo.targetSdkVersion = targetSdkVersion;
+        applicationInfo.taskAffinity = taskAffinity;
+        applicationInfo.theme = theme;
+        applicationInfo.uid = uid;
+        applicationInfo.uiOptions = uiOptions;
+        applicationInfo.volumeUuid = applicationVolumeUuid;
+        applicationInfo.zygotePreloadName = zygotePreloadName;
+
+        applicationInfo.setBaseCodePath(baseCodePath);
+        applicationInfo.setBaseResourcePath(applicationInfoBaseResourcePath);
+        applicationInfo.setCodePath(applicationInfoCodePath);
+        applicationInfo.setResourcePath(applicationInfoResourcePath);
+        applicationInfo.setSplitCodePaths(splitCodePaths);
+        applicationInfo.setSplitResourcePaths(applicationInfoSplitResourcePaths);
+
+        return applicationInfo;
+    }
+
+    @Override
+    public PackageImpl setVersionCode(int versionCode) {
+        this.versionCode = versionCode;
+        return this;
+    }
+
+    @Override
+    public PackageImpl setHiddenUntilInstalled(boolean hidden) {
+        this.hiddenUntilInstalled = hidden;
+        return this;
+    }
+
+    @Override
+    public String getSeInfo() {
+        return seInfo;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoResourcePath() {
+        return applicationInfoResourcePath;
+    }
+
+    @Override
+    public boolean isForwardLocked() {
+        // TODO(b/135203078): Unused? Move to debug flag?
+        return false;
+    }
+
+    @Override
+    public byte[] getRestrictUpdateHash() {
+        return restrictUpdateHash;
+    }
+
+    @Override
+    public boolean hasComponentClassName(String className) {
+        if (activities != null) {
+            for (ParsedActivity parsedActivity : activities) {
+                if (Objects.equals(className, parsedActivity.className)) {
+                    return true;
+                }
+            }
+        }
+
+        if (receivers != null) {
+            for (ParsedActivity receiver : receivers) {
+                if (Objects.equals(className, receiver.className)) {
+                    return true;
+                }
+            }
+        }
+
+        if (providers != null) {
+            for (ParsedProvider provider : providers) {
+                if (Objects.equals(className, provider.className)) {
+                    return true;
+                }
+            }
+        }
+
+        if (services != null) {
+            for (ParsedService service : services) {
+                if (Objects.equals(className, service.className)) {
+                    return true;
+                }
+            }
+        }
+
+        if (instrumentations != null) {
+            for (ParsedInstrumentation instrumentation : instrumentations) {
+                if (Objects.equals(className, instrumentation.className)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean isDefaultToDeviceProtectedStorage() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
+                != 0;
+    }
+
+    @Override
+    public boolean isInternal() {
+        return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
+    }
+
+    @Override
+    public int getBaseRevisionCode() {
+        return baseRevisionCode;
+    }
+
+    @Override
+    public int[] getSplitRevisionCodes() {
+        return splitRevisionCodes;
+    }
+
+    @Override
+    public boolean canHaveOatDir() {
+        // The following app types CANNOT have oat directory
+        // - non-updated system apps
+        return !isSystem() || isUpdatedSystemApp();
+    }
+
+    @Override
+    public long getLatestPackageUseTimeInMills() {
+        long latestUse = 0L;
+        for (long use : lastPackageUsageTimeInMills) {
+            latestUse = Math.max(latestUse, use);
+        }
+        return latestUse;
+    }
+
+    @Override
+    public long getLatestForegroundPackageUseTimeInMills() {
+        int[] foregroundReasons = {
+                PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY,
+                PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE
+        };
+
+        long latestUse = 0L;
+        for (int reason : foregroundReasons) {
+            latestUse = Math.max(latestUse, lastPackageUsageTimeInMills[reason]);
+        }
+        return latestUse;
+    }
+
+    @Override
+    public boolean isCoreApp() {
+        return coreApp;
+    }
+
+    @Override
+    public String getVersionName() {
+        return versionName;
+    }
+
+    @Override
+    public PackageImpl setVersionCodeMajor(int versionCodeMajor) {
+        this.versionCodeMajor = versionCodeMajor;
+        return this;
+    }
+
+    @Override
+    public long[] getLastPackageUsageTimeInMills() {
+        return lastPackageUsageTimeInMills;
+    }
+
+    @Override
+    public String getDataDir() {
+        return dataDir;
+    }
+
+    @Override
+    public boolean isExternal() {
+        return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+    }
+
+    @Override
+    public List<String> getImplicitPermissions() {
+        return implicitPermissions == null ? Collections.emptyList() : implicitPermissions;
+    }
+
+    /**
+     * TODO(b/135203078): Remove, ensure b/140256621 is fixed or irrelevant
+     * TODO(b/140256621): Remove after fixing instant app check
+     * @deprecated This method always returns false because there's no paired set method
+     */
+    @Deprecated
+    @Override
+    public boolean isInstantApp() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
+    }
+
+    @Override
+    public boolean hasRequestedLegacyExternalStorage() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
+    }
+
+    @Override
+    public boolean isVendor() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    }
+
+    @Override
+    public boolean isProduct() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    }
+
+    @Override
+    public boolean isOem() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    }
+
+    @Override
+    public boolean isEncryptionAware() {
+        boolean isPartiallyDirectBootAware =
+                (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
+        return isDirectBootAware() || isPartiallyDirectBootAware;
+    }
+
+    @Override
+    public boolean isEmbeddedDexUsed() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoProcessName() {
+        return processName;
+    }
+
+    @Override
+    public List<String> getAllCodePathsExcludingResourceOnly() {
+        ArrayList<String> paths = new ArrayList<>();
+        if ((flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            paths.add(baseCodePath);
+        }
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            for (int i = 0; i < splitCodePaths.length; i++) {
+                if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                    paths.add(splitCodePaths[i]);
+                }
+            }
+        }
+        return paths;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoName() {
+        return name;
+    }
+
+    private boolean isSignedWithPlatformKey() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
+    }
+
+    private boolean usesNonSdkApi() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) != 0;
+    }
+
+    private boolean isPackageWhitelistedForHiddenApis() {
+        return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+    }
+
+    private boolean isAllowedToUseHiddenApis() {
+        if (isSignedWithPlatformKey()) {
+            return true;
+        } else if (isSystemApp() || isUpdatedSystemApp()) {
+            return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int getHiddenApiEnforcementPolicy() {
+        if (isAllowedToUseHiddenApis()) {
+            return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+        }
+
+        // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done
+        //  entirely through ApplicationInfo and shouldn't touch this specific class, but that
+        //  may not always hold true.
+//        if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) {
+//            return mHiddenApiPolicy;
+//        }
+        return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
+    }
+
+    @Nullable
+    @Override
+    public SparseArray<int[]> getSplitDependencies() {
+        return splitDependencies;
+    }
+
+    @Override
+    public boolean requestsIsolatedSplitLoading() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
+    }
+
+    @Deprecated
+    @Override
+    public String getAppInfoClassLoaderName() {
+        return classLoaderName;
+    }
+
+    @Override
+    public String getClassLoaderName() {
+        return classLoaderName;
+    }
+
+    @Override
+    public String[] getSplitClassLoaderNames() {
+        return splitClassLoaderNames;
+    }
+
+    @Override
+    public String getOverlayCategory() {
+        return overlayCategory;
+    }
+
+    @Override
+    public boolean isProfileableByShell() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0;
+    }
+
+    @Nullable
+    @Override
+    public List<ParsedActivityIntentInfo> getPreferredActivityFilters() {
+        return preferredActivityFilters;
+    }
+
+    @Override
+    public boolean isHiddenUntilInstalled() {
+        return hiddenUntilInstalled;
+    }
+
+    @Override
+    public int getMinSdkVersion() {
+        return minSdkVersion;
+    }
+
+    @Override
+    public String getRestrictedAccountType() {
+        return restrictedAccountType;
+    }
+
+    @Override
+    public String getRequiredAccountType() {
+        return requiredAccountType;
+    }
+
+    @Override
+    public int getInstallLocation() {
+        return installLocation;
+    }
+
+    @Override
+    public List<ParsedActivity> getReceivers() {
+        return receivers;
+    }
+
+    @Override
+    public List<ParsedService> getServices() {
+        return services;
+    }
+
+    @Override
+    public List<ParsedProvider> getProviders() {
+        return providers;
+    }
+
+    @Override
+    public int getSharedUserLabel() {
+        return sharedUserLabel;
+    }
+
+    @Override
+    public int getVersionCodeMajor() {
+        return versionCodeMajor;
+    }
+
+    @Override
+    public boolean isRequiredForAllUsers() {
+        return requiredForAllUsers;
+    }
+
+    @Override
+    public int getCompileSdkVersion() {
+        return compileSdkVersion;
+    }
+
+    @Override
+    public String getCompileSdkVersionCodeName() {
+        return compileSdkVersionCodename;
+    }
+
+    @Nullable
+    @Override
+    public List<ConfigurationInfo> getConfigPreferences() {
+        return configPreferences;
+    }
+
+    @Nullable
+    @Override
+    public List<FeatureInfo> getReqFeatures() {
+        return reqFeatures;
+    }
+
+    @Override
+    public List<FeatureGroupInfo> getFeatureGroups() {
+        return featureGroups;
+    }
+
+    @Override
+    public String getDeviceProtectedDataDir() {
+        return deviceProtectedDataDir;
+    }
+
+    @Override
+    public String getCredentialProtectedDataDir() {
+        return credentialProtectedDataDir;
+    }
+
+    @Override
+    public String getSeInfoUser() {
+        return seInfoUser;
+    }
+
+    @Override
+    public String getClassName() {
+        return className;
+    }
+
+    @Override
+    public int getTheme() {
+        return theme;
+    }
+
+    @Override
+    public int getRequiresSmallestWidthDp() {
+        return requiresSmallestWidthDp;
+    }
+
+    @Override
+    public int getCompatibleWidthLimitDp() {
+        return compatibleWidthLimitDp;
+    }
+
+    @Override
+    public int getLargestWidthLimitDp() {
+        return largestWidthLimitDp;
+    }
+
+    @Override
+    public String getScanSourceDir() {
+        return applicationInfoCodePath;
+    }
+
+    @Override
+    public String getScanPublicSourceDir() {
+        return applicationInfoResourcePath;
+    }
+
+    @Override
+    public String getPublicSourceDir() {
+        return applicationInfoBaseResourcePath;
+    }
+
+    @Override
+    public String[] getSplitPublicSourceDirs() {
+        return applicationInfoSplitResourcePaths;
+    }
+
+    @Override
+    public String getSecondaryNativeLibraryDir() {
+        return secondaryNativeLibraryDir;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    @Override
+    public String getManageSpaceActivityName() {
+        return manageSpaceActivityName;
+    }
+
+    @Override
+    public int getDescriptionRes() {
+        return descriptionRes;
+    }
+
+    @Override
+    public String getBackupAgentName() {
+        return backupAgentName;
+    }
+
+    @Override
+    public int getFullBackupContent() {
+        return fullBackupContent;
+    }
+
+    @Override
+    public int getNetworkSecurityConfigRes() {
+        return networkSecurityConfigRes;
+    }
+
+    @Override
+    public int getCategory() {
+        return category;
+    }
+
+    @Override
+    public int getTargetSandboxVersion() {
+        return targetSandboxVersion;
+    }
+
+    @Override
+    public String getAppComponentFactory() {
+        return appComponentFactory;
+    }
+
+    @Override
+    public int getIconRes() {
+        return iconRes;
+    }
+
+    @Override
+    public int getRoundIconRes() {
+        return roundIconRes;
+    }
+
+    @Override
+    public String getZygotePreloadName() {
+        return zygotePreloadName;
+    }
+
+    @Override
+    public int getLabelRes() {
+        return labelRes;
+    }
+
+    @Override
+    public CharSequence getNonLocalizedLabel() {
+        return nonLocalizedLabel;
+    }
+
+    @Override
+    public int getIcon() {
+        return icon;
+    }
+
+    @Override
+    public int getBanner() {
+        return banner;
+    }
+
+    @Override
+    public int getLogo() {
+        return logo;
+    }
+
+    @Override
+    public Bundle getMetaData() {
+        return appMetaData;
+    }
+
+    @Override
+    @Nullable
+    public List<Intent> getQueriesIntents() {
+        return queriesIntents;
+    }
+
+    @Override
+    @Nullable
+    public List<String> getQueriesPackages() {
+        return queriesPackages;
+    }
+
+    private static void internStringArrayList(List<String> list) {
+        if (list != null) {
+            final int N = list.size();
+            for (int i = 0; i < N; ++i) {
+                list.set(i, list.get(i).intern());
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.supportsSmallScreens);
+        dest.writeInt(this.supportsNormalScreens);
+        dest.writeInt(this.supportsLargeScreens);
+        dest.writeInt(this.supportsXLargeScreens);
+        dest.writeInt(this.resizeable);
+        dest.writeInt(this.anyDensity);
+        dest.writeLongArray(this.lastPackageUsageTimeInMills);
+        dest.writeInt(this.versionCode);
+        dest.writeInt(this.versionCodeMajor);
+        dest.writeInt(this.baseRevisionCode);
+        dest.writeString(this.versionName);
+        dest.writeBoolean(this.coreApp);
+        dest.writeInt(this.compileSdkVersion);
+        dest.writeString(this.compileSdkVersionCodename);
+        dest.writeString(this.packageName);
+        dest.writeString(this.realPackage);
+        dest.writeString(this.manifestPackageName);
+        dest.writeString(this.baseCodePath);
+        dest.writeBoolean(this.requiredForAllUsers);
+        dest.writeString(this.restrictedAccountType);
+        dest.writeString(this.requiredAccountType);
+        dest.writeBoolean(this.baseHardwareAccelerated);
+        dest.writeString(this.overlayTarget);
+        dest.writeString(this.overlayTargetName);
+        dest.writeString(this.overlayCategory);
+        dest.writeInt(this.overlayPriority);
+        dest.writeBoolean(this.overlayIsStatic);
+        dest.writeString(this.staticSharedLibName);
+        dest.writeLong(this.staticSharedLibVersion);
+        dest.writeStringList(this.libraryNames);
+        dest.writeStringList(this.usesLibraries);
+        dest.writeStringList(this.usesOptionalLibraries);
+        dest.writeStringList(this.usesStaticLibraries);
+        dest.writeLongArray(this.usesStaticLibrariesVersions);
+
+        if (this.usesStaticLibrariesCertDigests == null) {
+            dest.writeInt(-1);
+        } else {
+            dest.writeInt(this.usesStaticLibrariesCertDigests.length);
+            for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
+                dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]);
+            }
+        }
+
+        dest.writeString(this.sharedUserId);
+        dest.writeInt(this.sharedUserLabel);
+        dest.writeTypedList(this.configPreferences);
+        dest.writeTypedList(this.reqFeatures);
+        dest.writeTypedList(this.featureGroups);
+        dest.writeByteArray(this.restrictUpdateHash);
+        dest.writeStringList(this.originalPackages);
+        dest.writeStringList(this.adoptPermissions);
+        dest.writeStringList(this.requestedPermissions);
+        dest.writeStringList(this.implicitPermissions);
+        dest.writeArraySet(this.upgradeKeySets);
+        dest.writeMap(this.keySetMapping);
+        dest.writeStringList(this.protectedBroadcasts);
+        dest.writeTypedList(this.activities);
+        dest.writeTypedList(this.receivers);
+        dest.writeTypedList(this.services);
+        dest.writeTypedList(this.providers);
+        dest.writeTypedList(this.permissions);
+        dest.writeTypedList(this.permissionGroups);
+        dest.writeTypedList(this.instrumentations);
+        ParsedIntentInfo.writeIntentsList(this.preferredActivityFilters, dest, flags);
+        dest.writeBundle(this.appMetaData);
+        dest.writeString(this.volumeUuid);
+        dest.writeString(this.applicationVolumeUuid);
+        dest.writeParcelable(this.signingDetails, flags);
+        dest.writeString(this.codePath);
+        dest.writeBoolean(this.use32BitAbi);
+        dest.writeBoolean(this.visibleToInstantApps);
+        dest.writeString(this.cpuAbiOverride);
+        dest.writeBoolean(this.isStub);
+        dest.writeInt(this.preferredOrder);
+        dest.writeBoolean(this.forceQueryable);
+        dest.writeParcelableList(this.queriesIntents, flags);
+        dest.writeStringList(this.queriesPackages);
+        dest.writeString(this.applicationInfoBaseResourcePath);
+        dest.writeString(this.applicationInfoCodePath);
+        dest.writeString(this.applicationInfoResourcePath);
+        dest.writeStringArray(this.applicationInfoSplitResourcePaths);
+        dest.writeString(this.appComponentFactory);
+        dest.writeString(this.backupAgentName);
+        dest.writeInt(this.banner);
+        dest.writeInt(this.category);
+        dest.writeString(this.classLoaderName);
+        dest.writeString(this.className);
+        dest.writeInt(this.compatibleWidthLimitDp);
+        dest.writeString(this.credentialProtectedDataDir);
+        dest.writeString(this.dataDir);
+        dest.writeInt(this.descriptionRes);
+        dest.writeString(this.deviceProtectedDataDir);
+        dest.writeBoolean(this.enabled);
+        dest.writeInt(this.flags);
+        dest.writeInt(this.fullBackupContent);
+        dest.writeBoolean(this.hiddenUntilInstalled);
+        dest.writeInt(this.icon);
+        dest.writeInt(this.iconRes);
+        dest.writeInt(this.installLocation);
+        dest.writeInt(this.labelRes);
+        dest.writeInt(this.largestWidthLimitDp);
+        dest.writeInt(this.logo);
+        dest.writeString(this.manageSpaceActivityName);
+        dest.writeFloat(this.maxAspectRatio);
+        dest.writeFloat(this.minAspectRatio);
+        dest.writeInt(this.minSdkVersion);
+        dest.writeString(this.name);
+        dest.writeString(this.nativeLibraryDir);
+        dest.writeString(this.nativeLibraryRootDir);
+        dest.writeBoolean(this.nativeLibraryRootRequiresIsa);
+        dest.writeInt(this.networkSecurityConfigRes);
+        dest.writeCharSequence(this.nonLocalizedLabel);
+        dest.writeString(this.permission);
+        dest.writeString(this.primaryCpuAbi);
+        dest.writeInt(this.privateFlags);
+        dest.writeString(this.processName);
+        dest.writeInt(this.requiresSmallestWidthDp);
+        dest.writeInt(this.roundIconRes);
+        dest.writeString(this.secondaryCpuAbi);
+        dest.writeString(this.secondaryNativeLibraryDir);
+        dest.writeString(this.seInfo);
+        dest.writeString(this.seInfoUser);
+        dest.writeInt(this.targetSandboxVersion);
+        dest.writeInt(this.targetSdkVersion);
+        dest.writeString(this.taskAffinity);
+        dest.writeInt(this.theme);
+        dest.writeInt(this.uid);
+        dest.writeInt(this.uiOptions);
+        dest.writeStringArray(this.usesLibraryFiles);
+        dest.writeTypedList(this.usesLibraryInfos);
+        dest.writeString(this.zygotePreloadName);
+        dest.writeStringArray(this.splitClassLoaderNames);
+        dest.writeStringArray(this.splitCodePaths);
+        dest.writeSparseArray(this.splitDependencies);
+        dest.writeIntArray(this.splitFlags);
+        dest.writeStringArray(this.splitNames);
+        dest.writeIntArray(this.splitRevisionCodes);
+    }
+
+    public PackageImpl(Parcel in) {
+        // We use the boot classloader for all classes that we load.
+        final ClassLoader boot = Object.class.getClassLoader();
+        this.supportsSmallScreens = in.readInt();
+        this.supportsNormalScreens = in.readInt();
+        this.supportsLargeScreens = in.readInt();
+        this.supportsXLargeScreens = in.readInt();
+        this.resizeable = in.readInt();
+        this.anyDensity = in.readInt();
+        this.lastPackageUsageTimeInMills = in.createLongArray();
+        this.versionCode = in.readInt();
+        this.versionCodeMajor = in.readInt();
+        this.baseRevisionCode = in.readInt();
+        this.versionName = TextUtils.safeIntern(in.readString());
+        this.coreApp = in.readBoolean();
+        this.compileSdkVersion = in.readInt();
+        this.compileSdkVersionCodename = TextUtils.safeIntern(in.readString());
+        this.packageName = TextUtils.safeIntern(in.readString());
+        this.realPackage = in.readString();
+        this.manifestPackageName = in.readString();
+        this.baseCodePath = in.readString();
+        this.requiredForAllUsers = in.readBoolean();
+        this.restrictedAccountType = in.readString();
+        this.requiredAccountType = in.readString();
+        this.baseHardwareAccelerated = in.readBoolean();
+        this.overlayTarget = in.readString();
+        this.overlayTargetName = in.readString();
+        this.overlayCategory = in.readString();
+        this.overlayPriority = in.readInt();
+        this.overlayIsStatic = in.readBoolean();
+        this.staticSharedLibName = TextUtils.safeIntern(in.readString());
+        this.staticSharedLibVersion = in.readLong();
+        this.libraryNames = in.createStringArrayList();
+        internStringArrayList(this.libraryNames);
+        this.usesLibraries = in.createStringArrayList();
+        internStringArrayList(this.usesLibraries);
+        this.usesOptionalLibraries = in.createStringArrayList();
+        internStringArrayList(this.usesOptionalLibraries);
+        this.usesStaticLibraries = in.createStringArrayList();
+        internStringArrayList(usesStaticLibraries);
+        this.usesStaticLibrariesVersions = in.createLongArray();
+
+        int digestsSize = in.readInt();
+        if (digestsSize >= 0) {
+            this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+            for (int index = 0; index < digestsSize; index++) {
+                this.usesStaticLibrariesCertDigests[index] = in.readStringArray();
+            }
+        }
+
+        this.sharedUserId = TextUtils.safeIntern(in.readString());
+        this.sharedUserLabel = in.readInt();
+        this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
+        this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
+        this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
+        this.restrictUpdateHash = in.createByteArray();
+        this.originalPackages = in.createStringArrayList();
+        this.adoptPermissions = in.createStringArrayList();
+        this.requestedPermissions = in.createStringArrayList();
+        internStringArrayList(this.requestedPermissions);
+        this.implicitPermissions = in.createStringArrayList();
+        internStringArrayList(this.implicitPermissions);
+        this.upgradeKeySets = (ArraySet<String>) in.readArraySet(boot);
+        this.keySetMapping = in.readHashMap(boot);
+        this.protectedBroadcasts = in.createStringArrayList();
+        internStringArrayList(this.protectedBroadcasts);
+        this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
+        this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
+        this.services = in.createTypedArrayList(ParsedService.CREATOR);
+        this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
+        this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
+        this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
+        this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
+        this.preferredActivityFilters = ParsedIntentInfo.createIntentsList(in);
+        this.appMetaData = in.readBundle(boot);
+        this.volumeUuid = in.readString();
+        this.applicationVolumeUuid = in.readString();
+        this.signingDetails = in.readParcelable(boot);
+        this.codePath = in.readString();
+        this.use32BitAbi = in.readBoolean();
+        this.visibleToInstantApps = in.readBoolean();
+        this.cpuAbiOverride = in.readString();
+        this.isStub = in.readBoolean();
+        this.preferredOrder = in.readInt();
+        this.forceQueryable = in.readBoolean();
+        this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
+        this.queriesPackages = in.createStringArrayList();
+        internStringArrayList(this.queriesPackages);
+        this.applicationInfoBaseResourcePath = in.readString();
+        this.applicationInfoCodePath = in.readString();
+        this.applicationInfoResourcePath = in.readString();
+        this.applicationInfoSplitResourcePaths = in.createStringArray();
+        this.appComponentFactory = in.readString();
+        this.backupAgentName = in.readString();
+        this.banner = in.readInt();
+        this.category = in.readInt();
+        this.classLoaderName = in.readString();
+        this.className = in.readString();
+        this.compatibleWidthLimitDp = in.readInt();
+        this.credentialProtectedDataDir = in.readString();
+        this.dataDir = in.readString();
+        this.descriptionRes = in.readInt();
+        this.deviceProtectedDataDir = in.readString();
+        this.enabled = in.readBoolean();
+        this.flags = in.readInt();
+        this.fullBackupContent = in.readInt();
+        this.hiddenUntilInstalled = in.readBoolean();
+        this.icon = in.readInt();
+        this.iconRes = in.readInt();
+        this.installLocation = in.readInt();
+        this.labelRes = in.readInt();
+        this.largestWidthLimitDp = in.readInt();
+        this.logo = in.readInt();
+        this.manageSpaceActivityName = in.readString();
+        this.maxAspectRatio = in.readFloat();
+        this.minAspectRatio = in.readFloat();
+        this.minSdkVersion = in.readInt();
+        this.name = in.readString();
+        this.nativeLibraryDir = in.readString();
+        this.nativeLibraryRootDir = in.readString();
+        this.nativeLibraryRootRequiresIsa = in.readBoolean();
+        this.networkSecurityConfigRes = in.readInt();
+        this.nonLocalizedLabel = in.readCharSequence();
+        this.permission = TextUtils.safeIntern(in.readString());
+        this.primaryCpuAbi = in.readString();
+        this.privateFlags = in.readInt();
+        this.processName = in.readString();
+        this.requiresSmallestWidthDp = in.readInt();
+        this.roundIconRes = in.readInt();
+        this.secondaryCpuAbi = in.readString();
+        this.secondaryNativeLibraryDir = in.readString();
+        this.seInfo = in.readString();
+        this.seInfoUser = in.readString();
+        this.targetSandboxVersion = in.readInt();
+        this.targetSdkVersion = in.readInt();
+        this.taskAffinity = in.readString();
+        this.theme = in.readInt();
+        this.uid = in.readInt();
+        this.uiOptions = in.readInt();
+        this.usesLibraryFiles = in.createStringArray();
+        this.usesLibraryInfos = in.createTypedArrayList(SharedLibraryInfo.CREATOR);
+        this.zygotePreloadName = in.readString();
+        this.splitClassLoaderNames = in.createStringArray();
+        this.splitCodePaths = in.createStringArray();
+        this.splitDependencies = in.readSparseArray(boot);
+        this.splitFlags = in.createIntArray();
+        this.splitNames = in.createStringArray();
+        this.splitRevisionCodes = in.createIntArray();
+    }
+
+    public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
+        @Override
+        public PackageImpl createFromParcel(Parcel source) {
+            return new PackageImpl(source);
+        }
+
+        @Override
+        public PackageImpl[] newArray(int size) {
+            return new PackageImpl[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
new file mode 100644
index 0000000..7b329eb
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FallbackCategoryProvider;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.SELinuxUtil;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Set;
+
+/** @hide */
+public class PackageInfoUtils {
+
+    private static final String TAG = ApkParseUtils.TAG;
+
+    /**
+     * Returns true if the package is installed and not hidden, or if the caller
+     * explicitly wanted all uninstalled and hidden packages as well.
+     */
+    private static boolean checkUseInstalledOrHidden(AndroidPackage pkg, PackageUserState state,
+            @PackageManager.PackageInfoFlags int flags) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.installed
+                && pkg.isHiddenUntilInstalled()) {
+            return false;
+        }
+
+        // If available for the target user, or trying to match uninstalled packages and it's
+        // a system app.
+        return state.isAvailable(flags)
+                || (pkg.isSystemApp()
+                && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
+    }
+
+    public static PackageInfo generate(AndroidPackage pkg, int[] gids,
+            @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+            Set<String> grantedPermissions, PackageUserState state, int userId) {
+        if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) {
+            return null;
+        }
+        ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = pkg.getPackageName();
+        pi.splitNames = pkg.getSplitNames();
+        pi.versionCode = pkg.getVersionCode();
+        pi.versionCodeMajor = pkg.getVersionCodeMajor();
+        pi.baseRevisionCode = pkg.getBaseRevisionCode();
+        pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
+        pi.versionName = pkg.getVersionName();
+        pi.sharedUserId = pkg.getSharedUserId();
+        pi.sharedUserLabel = pkg.getSharedUserLabel();
+        pi.applicationInfo = applicationInfo;
+        pi.installLocation = pkg.getInstallLocation();
+        pi.isStub = pkg.isStub();
+        pi.coreApp = pkg.isCoreApp();
+        if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
+        }
+        pi.restrictedAccountType = pkg.getRestrictedAccountType();
+        pi.requiredAccountType = pkg.getRequiredAccountType();
+        pi.overlayTarget = pkg.getOverlayTarget();
+        pi.targetOverlayableName = pkg.getOverlayTargetName();
+        pi.overlayCategory = pkg.getOverlayCategory();
+        pi.overlayPriority = pkg.getOverlayPriority();
+        pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
+        pi.compileSdkVersion = pkg.getCompileSdkVersion();
+        pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+        pi.firstInstallTime = firstInstallTime;
+        pi.lastUpdateTime = lastUpdateTime;
+        if ((flags & PackageManager.GET_GIDS) != 0) {
+            pi.gids = gids;
+        }
+        if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+            int size = pkg.getConfigPreferences() != null ? pkg.getConfigPreferences().size() : 0;
+            if (size > 0) {
+                pi.configPreferences = new ConfigurationInfo[size];
+                pkg.getConfigPreferences().toArray(pi.configPreferences);
+            }
+            size = pkg.getReqFeatures() != null ? pkg.getReqFeatures().size() : 0;
+            if (size > 0) {
+                pi.reqFeatures = new FeatureInfo[size];
+                pkg.getReqFeatures().toArray(pi.reqFeatures);
+            }
+            size = pkg.getFeatureGroups() != null ? pkg.getFeatureGroups().size() : 0;
+            if (size > 0) {
+                pi.featureGroups = new FeatureGroupInfo[size];
+                pkg.getFeatureGroups().toArray(pi.featureGroups);
+            }
+        }
+        if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+            if (pkg.getActivities() != null) {
+                final int N = pkg.getActivities().size();
+                if (N > 0) {
+                    int num = 0;
+                    final ActivityInfo[] res = new ActivityInfo[N];
+                    for (int i = 0; i < N; i++) {
+                        final ParsedActivity a = pkg.getActivities().get(i);
+                        if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) {
+                            if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+                                    a.className)) {
+                                continue;
+                            }
+                            res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
+                                    userId);
+                        }
+                    }
+                    pi.activities = ArrayUtils.trimToSize(res, num);
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+            if (pkg.getReceivers() != null) {
+                final int size = pkg.getReceivers().size();
+                if (size > 0) {
+                    int num = 0;
+                    final ActivityInfo[] res = new ActivityInfo[size];
+                    for (int i = 0; i < size; i++) {
+                        final ParsedActivity a = pkg.getReceivers().get(i);
+                        if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) {
+                            res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
+                                    userId);
+                        }
+                    }
+                    pi.receivers = ArrayUtils.trimToSize(res, num);
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_SERVICES) != 0) {
+            if (pkg.getServices() != null) {
+                final int size = pkg.getServices().size();
+                if (size > 0) {
+                    int num = 0;
+                    final ServiceInfo[] res = new ServiceInfo[size];
+                    for (int i = 0; i < size; i++) {
+                        final ComponentParseUtils.ParsedService s = pkg.getServices().get(i);
+                        if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), s, flags)) {
+                            res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
+                                    userId);
+                        }
+                    }
+                    pi.services = ArrayUtils.trimToSize(res, num);
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+            if (pkg.getProviders() != null) {
+                final int size = pkg.getProviders().size();
+                if (size > 0) {
+                    int num = 0;
+                    final ProviderInfo[] res = new ProviderInfo[size];
+                    for (int i = 0; i < size; i++) {
+                        final ComponentParseUtils.ParsedProvider pr = pkg.getProviders()
+                                .get(i);
+                        if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), pr, flags)) {
+                            res[num++] = generateProviderInfo(pkg, pr, flags, state,
+                                    applicationInfo, userId);
+                        }
+                    }
+                    pi.providers = ArrayUtils.trimToSize(res, num);
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+            if (pkg.getInstrumentations() != null) {
+                int N = pkg.getInstrumentations().size();
+                if (N > 0) {
+                    pi.instrumentation = new InstrumentationInfo[N];
+                    for (int i = 0; i < N; i++) {
+                        pi.instrumentation[i] = generateInstrumentationInfo(
+                                pkg.getInstrumentations().get(i), flags);
+                    }
+                }
+            }
+        }
+        if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+            if (pkg.getPermissions() != null) {
+                int N = ArrayUtils.size(pkg.getPermissions());
+                if (N > 0) {
+                    pi.permissions = new PermissionInfo[N];
+                    for (int i = 0; i < N; i++) {
+                        pi.permissions[i] = generatePermissionInfo(
+                                pkg.getPermissions().get(i),
+                                flags
+                        );
+                    }
+                }
+            }
+            if (pkg.getRequestedPermissions() != null) {
+                int N = pkg.getRequestedPermissions().size();
+                if (N > 0) {
+                    pi.requestedPermissions = new String[N];
+                    pi.requestedPermissionsFlags = new int[N];
+                    for (int i = 0; i < N; i++) {
+                        final String perm = pkg.getRequestedPermissions().get(i);
+                        pi.requestedPermissions[i] = perm;
+                        // The notion of required permissions is deprecated but for compatibility.
+                        pi.requestedPermissionsFlags[i] |=
+                                PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                        if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+                            pi.requestedPermissionsFlags[i] |=
+                                    PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                        }
+                    }
+                }
+            }
+        }
+
+        PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+        // deprecated method of getting signing certificates
+        if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+            if (signingDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+            } else if (signingDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = signingDetails.signatures.length;
+                pi.signatures = new Signature[numberOfSigs];
+                System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+                        numberOfSigs);
+            }
+        }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(signingDetails);
+            } else {
+                pi.signingInfo = null;
+            }
+        }
+
+        return pi;
+    }
+
+    // TODO(b/135203078): Remove this in favor of AndroidPackage.toAppInfo()
+    private static ApplicationInfo appInfoFromFinalPackage(AndroidPackage pkg) {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.name = pkg.getName();
+        if (appInfo.name != null) appInfo.name = appInfo.name.trim();
+        appInfo.packageName = pkg.getPackageName();
+        appInfo.labelRes = pkg.getLabelRes();
+        appInfo.nonLocalizedLabel = pkg.getNonLocalizedLabel();
+        if (appInfo.nonLocalizedLabel != null) {
+            appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
+        }
+        appInfo.icon = pkg.getIcon();
+        appInfo.banner = pkg.getBanner();
+        appInfo.logo = pkg.getLogo();
+        appInfo.metaData = pkg.getMetaData();
+
+        // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
+//        appInfo.showUserIcon = pkg.getShowUserIcon();
+
+        appInfo.taskAffinity = pkg.getTaskAffinity();
+        appInfo.permission = pkg.getPermission();
+        appInfo.processName = pkg.getProcessName();
+        appInfo.className = pkg.getClassName();
+        appInfo.theme = pkg.getTheme();
+        appInfo.flags = pkg.getFlags();
+        appInfo.privateFlags = pkg.getPrivateFlags();
+        appInfo.requiresSmallestWidthDp = pkg.getRequiresSmallestWidthDp();
+        appInfo.compatibleWidthLimitDp = pkg.getCompatibleWidthLimitDp();
+        appInfo.largestWidthLimitDp = pkg.getLargestWidthLimitDp();
+        appInfo.volumeUuid = pkg.getVolumeUuid();
+        appInfo.storageUuid = pkg.getStorageUuid();
+        appInfo.scanSourceDir = pkg.getScanSourceDir();
+        appInfo.scanPublicSourceDir = pkg.getScanPublicSourceDir();
+        appInfo.sourceDir = pkg.getBaseCodePath();
+        appInfo.publicSourceDir = pkg.getPublicSourceDir();
+        appInfo.splitNames = pkg.getSplitNames();
+        appInfo.splitSourceDirs = pkg.getSplitCodePaths();
+        appInfo.splitPublicSourceDirs = pkg.getSplitPublicSourceDirs();
+        appInfo.splitDependencies = pkg.getSplitDependencies();
+        appInfo.nativeLibraryDir = pkg.getNativeLibraryDir();
+        appInfo.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
+        appInfo.nativeLibraryRootDir = pkg.getNativeLibraryRootDir();
+        appInfo.nativeLibraryRootRequiresIsa = pkg.isNativeLibraryRootRequiresIsa();
+        appInfo.primaryCpuAbi = pkg.getPrimaryCpuAbi();
+        appInfo.secondaryCpuAbi = pkg.getSecondaryCpuAbi();
+
+        // TODO(b/135203078): Unused?
+//        appInfo.resourceDirs = pkg.getResourceDirs();
+        appInfo.seInfo = pkg.getSeInfo();
+        appInfo.seInfoUser = pkg.getSeInfoUser();
+        appInfo.sharedLibraryFiles = pkg.getUsesLibraryFiles();
+        appInfo.sharedLibraryInfos = pkg.getUsesLibraryInfos();
+        appInfo.dataDir = pkg.getDataDir();
+        appInfo.deviceProtectedDataDir = pkg.getDeviceProtectedDataDir();
+        appInfo.credentialProtectedDataDir = pkg.getCredentialProtectedDataDir();
+        appInfo.uid = pkg.getUid();
+        appInfo.minSdkVersion = pkg.getMinSdkVersion();
+        appInfo.targetSdkVersion = pkg.getTargetSdkVersion();
+        appInfo.setVersionCode(pkg.getLongVersionCode());
+        appInfo.enabled = pkg.isEnabled();
+
+        // TODO(b/135203078): Unused?
+//        appInfo.enabledSetting = pkg.getEnabledSetting();
+        appInfo.installLocation = pkg.getInstallLocation();
+        appInfo.manageSpaceActivityName = pkg.getManageSpaceActivityName();
+        appInfo.descriptionRes = pkg.getDescriptionRes();
+        appInfo.uiOptions = pkg.getUiOptions();
+        appInfo.backupAgentName = pkg.getBackupAgentName();
+        appInfo.fullBackupContent = pkg.getFullBackupContent();
+        appInfo.networkSecurityConfigRes = pkg.getNetworkSecurityConfigRes();
+        appInfo.category = pkg.getCategory();
+        appInfo.targetSandboxVersion = pkg.getTargetSandboxVersion();
+        appInfo.classLoaderName = pkg.getClassLoaderName();
+        appInfo.splitClassLoaderNames = pkg.getSplitClassLoaderNames();
+        appInfo.appComponentFactory = pkg.getAppComponentFactory();
+        appInfo.iconRes = pkg.getIconRes();
+        appInfo.roundIconRes = pkg.getRoundIconRes();
+        appInfo.compileSdkVersion = pkg.getCompileSdkVersion();
+        appInfo.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+
+        // TODO(b/135203078): See PackageImpl#getHiddenApiEnforcementPolicy
+//        appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
+        appInfo.hiddenUntilInstalled = pkg.isHiddenUntilInstalled();
+        appInfo.zygotePreloadName = pkg.getZygotePreloadName();
+        return appInfo;
+    }
+
+    public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
+            @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+
+        if (pkg == null) return null;
+        if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) {
+            return null;
+        }
+        if (!copyNeeded(flags, pkg, state, null, userId)
+                && ((flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0
+                || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+            // TODO(b/135203078): This isn't applicable anymore, as AppInfo isn't cached and
+            //   always built new in toAppInfo(). Remove entire copyNeeded flow? Or find a way to
+            //   transiently cache AppInfo, since multiple calls in quick secession probably need
+            //   the same AppInfo.
+            // In this case it is safe to directly modify the internal ApplicationInfo state:
+            // - CompatibilityMode is global state, so will be the same for every call.
+            // - We only come in to here if the app should reported as installed; this is the
+            // default state, and we will do a copy otherwise.
+            // - The enable state will always be reported the same for the application across
+            // calls; the only exception is for the UNTIL_USED mode, and in that case we will
+            // be doing a copy.
+            ApplicationInfo applicationInfo = pkg.toAppInfo();
+            updateApplicationInfo(applicationInfo, flags, state);
+            return applicationInfo;
+        }
+
+        // Make shallow copy so we can store the metadata/libraries safely
+        ApplicationInfo ai = appInfoFromFinalPackage(pkg);
+        ai.initForUser(userId);
+        if ((flags & PackageManager.GET_META_DATA) != 0) {
+            ai.metaData = pkg.getAppMetaData();
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
+            ai.sharedLibraryFiles = pkg.getUsesLibraryFiles();
+            ai.sharedLibraryInfos = pkg.getUsesLibraryInfos();
+        }
+        if (state.stopped) {
+            ai.flags |= ApplicationInfo.FLAG_STOPPED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
+        }
+        updateApplicationInfo(ai, flags, state);
+
+        return ai;
+    }
+
+    private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+            @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (a == null) return null;
+        if (!checkUseInstalledOrHidden(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (!copyNeeded(flags, pkg, state, a.metaData, userId)) {
+            updateApplicationInfo(applicationInfo, flags, state);
+        }
+        // Make shallow copies so we can store the metadata safely
+        ActivityInfo ai = new ActivityInfo();
+        assignSharedFieldsForComponentInfo(ai, a);
+        ai.targetActivity = a.targetActivity;
+        ai.processName = a.getProcessName();
+        ai.exported = a.exported;
+        ai.theme = a.theme;
+        ai.uiOptions = a.uiOptions;
+        ai.parentActivityName = a.parentActivityName;
+        ai.permission = a.getPermission();
+        ai.taskAffinity = a.taskAffinity;
+        ai.flags = a.flags;
+        ai.privateFlags = a.privateFlags;
+        ai.launchMode = a.launchMode;
+        ai.documentLaunchMode = a.documentLaunchMode;
+        ai.maxRecents = a.maxRecents;
+        ai.configChanges = a.configChanges;
+        ai.softInputMode = a.softInputMode;
+        ai.persistableMode = a.persistableMode;
+        ai.lockTaskLaunchMode = a.lockTaskLaunchMode;
+        ai.screenOrientation = a.screenOrientation;
+        ai.resizeMode = a.resizeMode;
+        ai.maxAspectRatio = a.maxAspectRatio;
+        ai.minAspectRatio = a.minAspectRatio;
+        ai.requestedVrComponent = a.requestedVrComponent;
+        ai.rotationAnimation = a.rotationAnimation;
+        ai.colorMode = a.colorMode;
+        ai.windowLayout = a.windowLayout;
+        ai.metaData = a.metaData;
+        ai.applicationInfo = applicationInfo;
+        return ai;
+    }
+
+    public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
+            @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+        return generateActivityInfo(pkg, a, flags, state, null, userId);
+    }
+
+    private static ServiceInfo generateServiceInfo(AndroidPackage pkg,
+            ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags,
+            PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (s == null) return null;
+        if (!checkUseInstalledOrHidden(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (!copyNeeded(flags, pkg, state, s.metaData, userId)) {
+            updateApplicationInfo(applicationInfo, flags, state);
+        }
+        // Make shallow copies so we can store the metadata safely
+        ServiceInfo si = new ServiceInfo();
+        assignSharedFieldsForComponentInfo(si, s);
+        si.exported = s.exported;
+        si.flags = s.flags;
+        si.metaData = s.metaData;
+        si.permission = s.getPermission();
+        si.processName = s.getProcessName();
+        si.mForegroundServiceType = s.foregroundServiceType;
+        si.metaData = s.metaData;
+        si.applicationInfo = applicationInfo;
+        return si;
+    }
+
+    public static ServiceInfo generateServiceInfo(AndroidPackage pkg,
+            ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags,
+            PackageUserState state, int userId) {
+        return generateServiceInfo(pkg, s, flags, state, null, userId);
+    }
+
+    private static ProviderInfo generateProviderInfo(AndroidPackage pkg,
+            ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags,
+            PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) {
+        if (p == null) return null;
+        if (!checkUseInstalledOrHidden(pkg, state, flags)) {
+            return null;
+        }
+        if (applicationInfo == null) {
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+        }
+        if (!copyNeeded(flags, pkg, state, p.metaData, userId)
+                && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
+                || p.uriPermissionPatterns == null)) {
+            updateApplicationInfo(applicationInfo, flags, state);
+        }
+        // Make shallow copies so we can store the metadata safely
+        ProviderInfo pi = new ProviderInfo();
+        assignSharedFieldsForComponentInfo(pi, p);
+        pi.exported = p.exported;
+        pi.flags = p.flags;
+        pi.processName = p.getProcessName();
+        pi.authority = p.getAuthority();
+        pi.isSyncable = p.isSyncable;
+        pi.readPermission = p.getReadPermission();
+        pi.writePermission = p.getWritePermission();
+        pi.grantUriPermissions = p.grantUriPermissions;
+        pi.forceUriPermissions = p.forceUriPermissions;
+        pi.multiprocess = p.multiProcess;
+        pi.initOrder = p.initOrder;
+        pi.uriPermissionPatterns = p.uriPermissionPatterns;
+        pi.pathPermissions = p.pathPermissions;
+        pi.metaData = p.metaData;
+        if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+            pi.uriPermissionPatterns = null;
+        }
+        pi.applicationInfo = applicationInfo;
+        return pi;
+    }
+
+    public static ProviderInfo generateProviderInfo(AndroidPackage pkg,
+            ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags,
+            PackageUserState state, int userId) {
+        return generateProviderInfo(pkg, p, flags, state, null, userId);
+    }
+
+    public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (i == null) return null;
+
+        InstrumentationInfo ii = new InstrumentationInfo();
+        assignSharedFieldsForPackageItemInfo(ii, i);
+        ii.targetPackage = i.getTargetPackage();
+        ii.targetProcesses = i.getTargetProcesses();
+        ii.handleProfiling = i.handleProfiling;
+        ii.functionalTest = i.functionalTest;
+
+        ii.sourceDir = i.sourceDir;
+        ii.publicSourceDir = i.publicSourceDir;
+        ii.splitNames = i.splitNames;
+        ii.splitSourceDirs = i.splitSourceDirs;
+        ii.splitPublicSourceDirs = i.splitPublicSourceDirs;
+        ii.splitDependencies = i.splitDependencies;
+        ii.dataDir = i.dataDir;
+        ii.deviceProtectedDataDir = i.deviceProtectedDataDir;
+        ii.credentialProtectedDataDir = i.credentialProtectedDataDir;
+        ii.primaryCpuAbi = i.primaryCpuAbi;
+        ii.secondaryCpuAbi = i.secondaryCpuAbi;
+        ii.nativeLibraryDir = i.nativeLibraryDir;
+        ii.secondaryNativeLibraryDir = i.secondaryNativeLibraryDir;
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return ii;
+        }
+        ii.metaData = i.metaData;
+        return ii;
+    }
+
+    public static PermissionInfo generatePermissionInfo(ParsedPermission p,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (p == null) return null;
+
+        PermissionInfo pi = new PermissionInfo(p.backgroundPermission);
+        assignSharedFieldsForPackageItemInfo(pi, p);
+        pi.group = p.getGroup();
+        pi.requestRes = p.requestRes;
+        pi.protectionLevel = p.protectionLevel;
+        pi.descriptionRes = p.descriptionRes;
+        pi.flags = p.flags;
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return pi;
+        }
+        pi.metaData = p.metaData;
+        return pi;
+    }
+
+    public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
+            @PackageManager.ComponentInfoFlags int flags) {
+        if (pg == null) return null;
+
+        PermissionGroupInfo pgi = new PermissionGroupInfo(
+                pg.requestDetailResourceId,
+                pg.backgroundRequestResourceId,
+                pg.backgroundRequestDetailResourceId
+        );
+        assignSharedFieldsForPackageItemInfo(pgi, pg);
+        pgi.priority = pg.priority;
+        pgi.requestRes = pg.requestRes;
+        pgi.flags = pg.flags;
+
+        if ((flags & PackageManager.GET_META_DATA) == 0) {
+            return pgi;
+        }
+        pgi.metaData = pg.metaData;
+        return pgi;
+    }
+
+    private static boolean copyNeeded(@PackageManager.ComponentInfoFlags int flags,
+            AndroidPackage pkg, PackageUserState state, Bundle metaData, int userId) {
+        if (userId != UserHandle.USER_SYSTEM) {
+            // We always need to copy for other users, since we need
+            // to fix up the uid.
+            return true;
+        }
+        if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+            boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+            if (pkg.isEnabled() != enabled) {
+                return true;
+            }
+        }
+        boolean suspended = (pkg.getFlags() & FLAG_SUSPENDED) != 0;
+        if (state.suspended != suspended) {
+            return true;
+        }
+        if (!state.installed || state.hidden) {
+            return true;
+        }
+        if (state.stopped) {
+            return true;
+        }
+        if (state.instantApp != pkg.isInstantApp()) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_META_DATA) != 0
+                && (metaData != null || pkg.getAppMetaData() != null)) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+                && pkg.getUsesLibraryFiles() != null) {
+            return true;
+        }
+        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0
+                && pkg.getUsesLibraryInfos() != null) {
+            return true;
+        }
+        return pkg.getStaticSharedLibName() != null;
+    }
+
+    private static void updateApplicationInfo(ApplicationInfo ai,
+            @PackageManager.ApplicationInfoFlags int flags,
+            PackageUserState state) {
+        // CompatibilityMode is global state.
+        if (!PackageParser.sCompatibilityModeEnabled) {
+            ai.disableCompatibilityMode();
+        }
+        if (state.installed) {
+            ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+        }
+        if (state.suspended) {
+            ai.flags |= ApplicationInfo.FLAG_SUSPENDED;
+        } else {
+            ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED;
+        }
+        if (state.instantApp) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_INSTANT;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT;
+        }
+        if (state.virtualPreload) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        }
+        if (state.hidden) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+        }
+        if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+            ai.enabled = true;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+            ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+        } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+            ai.enabled = false;
+        }
+        ai.enabledSetting = state.enabled;
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = state.categoryHint;
+        }
+        if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+            ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+        }
+        ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
+        ai.resourceDirs = state.overlayPaths;
+        ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0)
+                ? ai.roundIconRes : ai.iconRes;
+    }
+
+    private static void assignSharedFieldsForPackageItemInfo(PackageItemInfo packageItemInfo,
+            ComponentParseUtils.ParsedComponent parsedComponent) {
+        packageItemInfo.banner = parsedComponent.banner;
+        packageItemInfo.icon = parsedComponent.icon;
+        packageItemInfo.labelRes = parsedComponent.labelRes;
+        packageItemInfo.logo = parsedComponent.logo;
+        packageItemInfo.name = parsedComponent.className;
+        packageItemInfo.nonLocalizedLabel = parsedComponent.nonLocalizedLabel;
+        packageItemInfo.packageName = parsedComponent.getPackageName();
+    }
+
+    private static void assignSharedFieldsForComponentInfo(ComponentInfo componentInfo,
+            ComponentParseUtils.ParsedComponent parsedComponent) {
+        assignSharedFieldsForPackageItemInfo(componentInfo, parsedComponent);
+        componentInfo.descriptionRes = parsedComponent.descriptionRes;
+        componentInfo.directBootAware = parsedComponent.directBootAware;
+        componentInfo.enabled = parsedComponent.enabled;
+        componentInfo.splitName = parsedComponent.getSplitName();
+    }
+
+}
diff --git a/core/java/android/content/pm/parsing/ParsedPackage.java b/core/java/android/content/pm/parsing/ParsedPackage.java
new file mode 100644
index 0000000..05cf586
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsedPackage.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import android.content.pm.PackageParser;
+
+/**
+ * Methods used for mutation after direct package parsing, mostly done inside
+ * {@link com.android.server.pm.PackageManagerService}.
+ *
+ * Java disallows defining this as an inner interface, so this must be a separate file.
+ *
+ * @hide
+ */
+public interface ParsedPackage extends AndroidPackage {
+
+    AndroidPackage hideAsFinal();
+
+    ParsedPackage addUsesLibrary(int index, String libraryName);
+
+    ParsedPackage addUsesOptionalLibrary(int index, String libraryName);
+
+    ParsedPackage capPermissionPriorities();
+
+    ParsedPackage clearAdoptPermissions();
+
+    ParsedPackage clearOriginalPackages();
+
+    ParsedPackage clearProtectedBroadcasts();
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #setCodePath(String)}
+     */
+    @Deprecated
+    ParsedPackage setApplicationInfoCodePath(String applicationInfoCodePath);
+
+    /**
+     * TODO(b/135203078): Use non-AppInfo method
+     * @deprecated use {@link #setCodePath(String)}
+     */
+    @Deprecated
+    ParsedPackage setApplicationInfoResourcePath(String applicationInfoResourcePath);
+
+    ParsedPackage setBaseCodePath(String baseCodePath);
+
+    ParsedPackage setCodePath(String codePath);
+
+    ParsedPackage setCpuAbiOverride(String cpuAbiOverride);
+
+    ParsedPackage setNativeLibraryDir(String nativeLibraryDir);
+
+    ParsedPackage setNativeLibraryRootDir(String nativeLibraryRootDir);
+
+    ParsedPackage setPackageName(String packageName);
+
+    ParsedPackage setPrimaryCpuAbi(String primaryCpuAbi);
+
+    ParsedPackage setProcessName(String processName);
+
+    ParsedPackage setRealPackage(String realPackage);
+
+    ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
+
+    ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+
+    ParsedPackage setSplitCodePaths(String[] splitCodePaths);
+
+    ParsedPackage initForUser(int userId);
+
+    ParsedPackage setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa);
+
+    ParsedPackage setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware);
+
+    ParsedPackage setFactoryTest(boolean factoryTest);
+
+    ParsedPackage markNotActivitiesAsNotExportedIfSingleUser();
+
+    ParsedPackage setOdm(boolean odm);
+
+    ParsedPackage setOem(boolean oem);
+
+    ParsedPackage setPrivileged(boolean privileged);
+
+    ParsedPackage setProduct(boolean product);
+
+    ParsedPackage setSignedWithPlatformKey(boolean signedWithPlatformKey);
+
+    ParsedPackage setSystem(boolean system);
+
+    ParsedPackage setSystemExt(boolean systemExt);
+
+    ParsedPackage setUpdatedSystemApp(boolean updatedSystemApp);
+
+    ParsedPackage setVendor(boolean vendor);
+
+    ParsedPackage removePermission(int index);
+
+    ParsedPackage removeUsesLibrary(String libraryName);
+
+    ParsedPackage removeUsesOptionalLibrary(String libraryName);
+
+    ParsedPackage setApplicationInfoBaseResourcePath(String applicationInfoBaseResourcePath);
+
+    ParsedPackage setApplicationInfoSplitResourcePaths(
+            String[] applicationInfoSplitResourcePaths);
+
+    ParsedPackage setApplicationVolumeUuid(String applicationVolumeUuid);
+
+    ParsedPackage setCoreApp(boolean coreApp);
+
+    ParsedPackage setIsStub(boolean isStub);
+
+    // TODO(b/135203078): Remove entirely
+    ParsedPackage setPackageSettingCallback(PackageSettingCallback packageSettingCallback);
+
+    ParsedPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
+
+    ParsedPackage setSeInfo(String seInfo);
+
+    ParsedPackage setSeInfoUser(String seInfoUser);
+
+    ParsedPackage setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir);
+
+    ParsedPackage setUid(int uid);
+
+    ParsedPackage setVersionCode(int versionCode);
+
+    ParsedPackage setVersionCodeMajor(int versionCodeMajor);
+
+    // TODO(b/135203078): Move logic earlier in parse chain so nothing needs to be reverted
+    ParsedPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage);
+
+    ParsedPackage setDirectBootAware(boolean directBootAware);
+
+    ParsedPackage setPersistent(boolean persistent);
+
+    interface PackageSettingCallback {
+        default void setAndroidPackage(AndroidPackage pkg){}
+    }
+}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
new file mode 100644
index 0000000..43c1f6e
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import java.security.PublicKey;
+
+/**
+ * Methods used for mutation during direct package parsing.
+ *
+ * Java disallows defining this as an inner interface, so this must be a separate file.
+ *
+ * @hide
+ */
+public interface ParsingPackage extends AndroidPackage {
+
+    ParsingPackage addActivity(ParsedActivity parsedActivity);
+
+    ParsingPackage addAdoptPermission(String adoptPermission);
+
+    ParsingPackage addConfigPreference(ConfigurationInfo configPreference);
+
+    ParsingPackage addFeatureGroup(FeatureGroupInfo featureGroup);
+
+    ParsingPackage addImplicitPermission(String permission);
+
+    ParsingPackage addInstrumentation(ParsedInstrumentation instrumentation);
+
+    ParsingPackage addKeySet(String keySetName, PublicKey publicKey);
+
+    ParsingPackage addLibraryName(String libraryName);
+
+    ParsingPackage addOriginalPackage(String originalPackage);
+
+    ParsingPackage addPermission(ParsedPermission permission);
+
+    ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
+
+    ParsingPackage addPreferredActivityFilter(ParsedActivityIntentInfo activityIntentInfo);
+
+    ParsingPackage addProtectedBroadcast(String protectedBroadcast);
+
+    ParsingPackage addProvider(ParsedProvider parsedProvider);
+
+    ParsingPackage addReceiver(ParsedActivity parsedReceiver);
+
+    ParsingPackage addReqFeature(FeatureInfo reqFeature);
+
+    ParsingPackage addRequestedPermission(String permission);
+
+    ParsingPackage addService(ParsedService parsedService);
+
+    ParsingPackage addUsesLibrary(String libraryName);
+
+    ParsingPackage addUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage addUsesStaticLibrary(String libraryName);
+
+    ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
+
+    ParsingPackage addUsesStaticLibraryVersion(long version);
+
+    ParsingPackage addQueriesIntent(Intent intent);
+
+    ParsingPackage addQueriesPackage(String packageName);
+
+    ParsingPackage asSplit(
+            String[] splitNames,
+            String[] splitCodePaths,
+            int[] splitRevisionCodes,
+            @Nullable SparseArray<int[]> splitDependencies
+    );
+
+    ParsingPackage setAppMetaData(Bundle appMetaData);
+
+    ParsingPackage setForceQueryable(boolean forceQueryable);
+
+    ParsingPackage setMaxAspectRatio(float maxAspectRatio);
+
+    ParsingPackage setMinAspectRatio(float minAspectRatio);
+
+    ParsingPackage setName(String name);
+
+    ParsingPackage setPermission(String permission);
+
+    ParsingPackage setProcessName(String processName);
+
+    ParsingPackage setSharedUserId(String sharedUserId);
+
+    ParsingPackage setStaticSharedLibName(String staticSharedLibName);
+
+    ParsingPackage setTaskAffinity(String taskAffinity);
+
+    ParsingPackage setTargetSdkVersion(int targetSdkVersion);
+
+    ParsingPackage setUiOptions(int uiOptions);
+
+    ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated);
+
+    ParsingPackage setActivitiesResizeModeResizeable(boolean resizeable);
+
+    ParsingPackage setActivitiesResizeModeResizeableViaSdkVersion(boolean resizeableViaSdkVersion);
+
+    ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture);
+
+    ParsingPackage setAllowBackup(boolean allowBackup);
+
+    ParsingPackage setAllowClearUserData(boolean allowClearUserData);
+
+    ParsingPackage setAllowClearUserDataOnFailedRestore(boolean allowClearUserDataOnFailedRestore);
+
+    ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting);
+
+    ParsingPackage setIsOverlay(boolean isOverlay);
+
+    ParsingPackage setBackupInForeground(boolean backupInForeground);
+
+    ParsingPackage setCantSaveState(boolean cantSaveState);
+
+    ParsingPackage setDebuggable(boolean debuggable);
+
+    ParsingPackage setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage);
+
+    ParsingPackage setDirectBootAware(boolean directBootAware);
+
+    ParsingPackage setExternalStorage(boolean externalStorage);
+
+    ParsingPackage setExtractNativeLibs(boolean extractNativeLibs);
+
+    ParsingPackage setFullBackupOnly(boolean fullBackupOnly);
+
+    ParsingPackage setHasCode(boolean hasCode);
+
+    ParsingPackage setHasFragileUserData(boolean hasFragileUserData);
+
+    ParsingPackage setIsGame(boolean isGame);
+
+    ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading);
+
+    ParsingPackage setKillAfterRestore(boolean killAfterRestore);
+
+    ParsingPackage setLargeHeap(boolean largeHeap);
+
+    ParsingPackage setMultiArch(boolean multiArch);
+
+    ParsingPackage setPartiallyDirectBootAware(boolean partiallyDirectBootAware);
+
+    ParsingPackage setPersistent(boolean persistent);
+
+    ParsingPackage setProfileableByShell(boolean profileableByShell);
+
+    ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
+
+    ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
+
+    ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
+
+    ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
+
+    ParsingPackage setSupportsRtl(boolean supportsRtl);
+
+    ParsingPackage setTestOnly(boolean testOnly);
+
+    ParsingPackage setUseEmbeddedDex(boolean useEmbeddedDex);
+
+    ParsingPackage setUsesCleartextTraffic(boolean usesCleartextTraffic);
+
+    ParsingPackage setUsesNonSdkApi(boolean usesNonSdkApi);
+
+    ParsingPackage setVisibleToInstantApps(boolean visibleToInstantApps);
+
+    ParsingPackage setVmSafeMode(boolean vmSafeMode);
+
+    ParsingPackage removeUsesOptionalLibrary(String libraryName);
+
+    ParsingPackage setAnyDensity(int anyDensity);
+
+    ParsingPackage setAppComponentFactory(String appComponentFactory);
+
+    ParsingPackage setApplicationVolumeUuid(String applicationVolumeUuid);
+
+    ParsingPackage setBackupAgentName(String backupAgentName);
+
+    ParsingPackage setBanner(int banner);
+
+    ParsingPackage setCategory(int category);
+
+    ParsingPackage setClassLoaderName(String classLoaderName);
+
+    ParsingPackage setClassName(String className);
+
+    ParsingPackage setCodePath(String codePath);
+
+    ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp);
+
+    ParsingPackage setDescriptionRes(int descriptionRes);
+
+    ParsingPackage setEnabled(boolean enabled);
+
+    ParsingPackage setFullBackupContent(int fullBackupContent);
+
+    ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
+
+    ParsingPackage setIcon(int icon);
+
+    ParsingPackage setIconRes(int iconRes);
+
+    ParsingPackage setInstallLocation(int installLocation);
+
+    ParsingPackage setLabelRes(int labelRes);
+
+    ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp);
+
+    ParsingPackage setLogo(int logo);
+
+    ParsingPackage setManageSpaceActivityName(String manageSpaceActivityName);
+
+    ParsingPackage setMinSdkVersion(int minSdkVersion);
+
+    ParsingPackage setNetworkSecurityConfigRes(int networkSecurityConfigRes);
+
+    ParsingPackage setNonLocalizedLabel(CharSequence nonLocalizedLabel);
+
+    ParsingPackage setOverlayCategory(String overlayCategory);
+
+    ParsingPackage setOverlayIsStatic(boolean overlayIsStatic);
+
+    ParsingPackage setOverlayPriority(int overlayPriority);
+
+    ParsingPackage setOverlayTarget(String overlayTarget);
+
+    ParsingPackage setOverlayTargetName(String overlayTargetName);
+
+    ParsingPackage setRealPackage(String realPackage);
+
+    ParsingPackage setRequiredAccountType(String requiredAccountType);
+
+    ParsingPackage setRequiredForAllUsers(boolean requiredForAllUsers);
+
+    ParsingPackage setRequiresSmallestWidthDp(int requiresSmallestWidthDp);
+
+    ParsingPackage setResizeable(int resizeable);
+
+    ParsingPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
+
+    ParsingPackage setRestrictedAccountType(String restrictedAccountType);
+
+    ParsingPackage setRoundIconRes(int roundIconRes);
+
+    ParsingPackage setSharedUserLabel(int sharedUserLabel);
+
+    ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+
+    ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
+
+    ParsingPackage setStaticSharedLibVersion(long staticSharedLibVersion);
+
+    ParsingPackage setSupportsLargeScreens(int supportsLargeScreens);
+
+    ParsingPackage setSupportsNormalScreens(int supportsNormalScreens);
+
+    ParsingPackage setSupportsSmallScreens(int supportsSmallScreens);
+
+    ParsingPackage setSupportsXLargeScreens(int supportsXLargeScreens);
+
+    ParsingPackage setTargetSandboxVersion(int targetSandboxVersion);
+
+    ParsingPackage setTheme(int theme);
+
+    ParsingPackage setUpgradeKeySets(ArraySet<String> upgradeKeySets);
+
+    ParsingPackage setUse32BitAbi(boolean use32BitAbi);
+
+    ParsingPackage setVolumeUuid(String volumeUuid);
+
+    ParsingPackage setZygotePreloadName(String zygotePreloadName);
+
+    ParsingPackage sortActivities();
+
+    ParsingPackage sortReceivers();
+
+    ParsingPackage sortServices();
+
+    ParsedPackage hideAsParsed();
+
+    ParsingPackage setBaseRevisionCode(int baseRevisionCode);
+
+    ParsingPackage setPreferredOrder(int preferredOrder);
+
+    ParsingPackage setVersionName(String versionName);
+
+    ParsingPackage setCompileSdkVersion(int compileSdkVersion);
+
+    ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename);
+
+    boolean usesCompatibilityMode();
+}
diff --git a/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
new file mode 100644
index 0000000..81b4bc5
--- /dev/null
+++ b/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
+ * and android.hidl.manager-V1.0-java libraries are included by default.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
+
+    @Override
+    public void updatePackage(ParsedPackage parsedPackage) {
+        // This was the default <= P and is maintained for backwards compatibility.
+        boolean isLegacy = parsedPackage.getTargetSdkVersion() <= Build.VERSION_CODES.P;
+        // Only system apps use these libraries
+        boolean isSystem = parsedPackage.isSystemApp() || parsedPackage.isUpdatedSystemApp();
+
+        if (isLegacy && isSystem) {
+            prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_BASE);
+            prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_MANAGER);
+        } else {
+            removeLibrary(parsedPackage, ANDROID_HIDL_BASE);
+            removeLibrary(parsedPackage, ANDROID_HIDL_MANAGER);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
similarity index 70%
rename from core/java/android/content/pm/AndroidTestBaseUpdater.java
rename to core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
index da1a693..25c3099 100644
--- a/core/java/android/content/pm/AndroidTestBaseUpdater.java
+++ b/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.content.pm;
+package android.content.pm.parsing.library;
 
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
 
-import android.content.pm.PackageParser.Package;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -38,23 +39,22 @@
 @VisibleForTesting
 public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
 
-    private static boolean apkTargetsApiLevelLessThanOrEqualToQ(Package pkg) {
-        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
-        return targetSdkVersion <= Build.VERSION_CODES.Q;
+    private static boolean apkTargetsApiLevelLessThanOrEqualToQ(AndroidPackage pkg) {
+        return pkg.getTargetSdkVersion() <= Build.VERSION_CODES.Q;
     }
 
     @Override
-    public void updatePackage(Package pkg) {
+    public void updatePackage(ParsedPackage parsedPackage) {
         // Packages targeted at <= Q expect the classes in the android.test.base library
         // to be accessible so this maintains backward compatibility by adding the
         // android.test.base library to those packages.
-        if (apkTargetsApiLevelLessThanOrEqualToQ(pkg)) {
-            prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
+        if (apkTargetsApiLevelLessThanOrEqualToQ(parsedPackage)) {
+            prefixRequiredLibrary(parsedPackage, ANDROID_TEST_BASE);
         } else {
             // If a package already depends on android.test.runner then add a dependency on
             // android.test.base because android.test.runner depends on classes from the
             // android.test.base library.
-            prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+            prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
         }
     }
 }
diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
similarity index 67%
rename from core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
rename to core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index 707443b..613a06b 100644
--- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.content.pm;
+package android.content.pm.parsing.library;
 
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
-import android.content.pm.PackageParser.Package;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.Build;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -31,18 +32,17 @@
 @VisibleForTesting
 public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
 
-    private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) {
-        int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
-        return targetSdkVersion < Build.VERSION_CODES.P;
+    private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(AndroidPackage pkg) {
+        return pkg.getTargetSdkVersion() < Build.VERSION_CODES.P;
     }
 
     @Override
-    public void updatePackage(Package pkg) {
+    public void updatePackage(ParsedPackage parsedPackage) {
         // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
         // to be accessible so this maintains backward compatibility by adding the
         // org.apache.http.legacy library to those packages.
-        if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
-            prefixRequiredLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
+        if (apkTargetsApiLevelLessThanOrEqualToOMR1(parsedPackage)) {
+            prefixRequiredLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY);
         }
     }
 }
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
similarity index 80%
rename from core/java/android/content/pm/PackageBackwardCompatibility.java
rename to core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
index 4331bd4..1220fc4 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package android.content.pm;
+package android.content.pm.parsing.library;
 
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
-import android.content.pm.PackageParser.Package;
+import android.content.pm.parsing.ParsedPackage;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -31,7 +31,7 @@
 import java.util.function.Supplier;
 
 /**
- * Modifies {@link Package} in order to maintain backwards compatibility.
+ * Modifies {@link ParsedPackage} in order to maintain backwards compatibility.
  *
  * @hide
  */
@@ -60,7 +60,7 @@
         // will remove any references to org.apache.http.library from the package so that it does
         // not try and load the library when it is on the bootclasspath.
         boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
-                "android.content.pm.AndroidTestBaseUpdater",
+                "android.content.pm.parsing.library.AndroidTestBaseUpdater",
                 RemoveUnnecessaryAndroidTestBaseLibrary::new);
 
         PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
@@ -123,20 +123,20 @@
     }
 
     /**
-     * Modify the shared libraries in the supplied {@link Package} to maintain backwards
+     * Modify the shared libraries in the supplied {@link ParsedPackage} to maintain backwards
      * compatibility.
      *
-     * @param pkg the {@link Package} to modify.
+     * @param parsedPackage the {@link ParsedPackage} to modify.
      */
     @VisibleForTesting
-    public static void modifySharedLibraries(Package pkg) {
-        INSTANCE.updatePackage(pkg);
+    public static void modifySharedLibraries(ParsedPackage parsedPackage) {
+        INSTANCE.updatePackage(parsedPackage);
     }
 
     @Override
-    public void updatePackage(Package pkg) {
+    public void updatePackage(ParsedPackage parsedPackage) {
         for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
-            packageUpdater.updatePackage(pkg);
+            packageUpdater.updatePackage(parsedPackage);
         }
     }
 
@@ -161,10 +161,10 @@
     public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(Package pkg) {
+        public void updatePackage(ParsedPackage parsedPackage) {
             // android.test.runner has a dependency on android.test.mock so if android.test.runner
             // is present but android.test.mock is not then add android.test.mock.
-            prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
+            prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
         }
     }
 
@@ -177,8 +177,8 @@
             extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(Package pkg) {
-            removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
+        public void updatePackage(ParsedPackage parsedPackage) {
+            removeLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY);
         }
 
     }
@@ -192,8 +192,8 @@
             extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(Package pkg) {
-            removeLibrary(pkg, ANDROID_TEST_BASE);
+        public void updatePackage(ParsedPackage parsedPackage) {
+            removeLibrary(parsedPackage, ANDROID_TEST_BASE);
         }
     }
 }
diff --git a/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
new file mode 100644
index 0000000..8b27d14
--- /dev/null
+++ b/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.parsing.library;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.ParsedPackage;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base for classes that update a {@link ParsedPackage}'s shared libraries.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public abstract class PackageSharedLibraryUpdater {
+
+    /**
+     * Update the package's shared libraries.
+     *
+     * @param parsedPackage the package to update.
+     */
+    public abstract void updatePackage(ParsedPackage parsedPackage);
+
+    static void removeLibrary(ParsedPackage parsedPackage, String libraryName) {
+        parsedPackage.removeUsesLibrary(libraryName)
+                .removeUsesOptionalLibrary(libraryName);
+    }
+
+    static @NonNull
+            <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
+        if (cur == null) {
+            cur = new ArrayList<>();
+        }
+        cur.add(0, val);
+        return cur;
+    }
+
+    private static boolean isLibraryPresent(List<String> usesLibraries,
+            List<String> usesOptionalLibraries, String apacheHttpLegacy) {
+        return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
+                || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
+    }
+
+    /**
+     * Add an implicit dependency.
+     *
+     * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with
+     * the {@code implicitDependency} if it is not already in the list of libraries.
+     *
+     * @param parsedPackage the {@link ParsedPackage} to update.
+     * @param existingLibrary the existing library.
+     * @param implicitDependency the implicit dependency to add
+     */
+    void prefixImplicitDependency(ParsedPackage parsedPackage, String existingLibrary,
+            String implicitDependency) {
+        List<String> usesLibraries = parsedPackage.getUsesLibraries();
+        List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries();
+
+        if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
+            if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
+                parsedPackage.addUsesLibrary(0, implicitDependency);
+            } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
+                parsedPackage.addUsesOptionalLibrary(0, implicitDependency);
+            }
+        }
+    }
+
+    void prefixRequiredLibrary(ParsedPackage parsedPackage, String libraryName) {
+        List<String> usesLibraries = parsedPackage.getUsesLibraries();
+        List<String> usesOptionalLibraries = parsedPackage.getUsesOptionalLibraries();
+
+        boolean alreadyPresent = isLibraryPresent(
+                usesLibraries, usesOptionalLibraries, libraryName);
+        if (!alreadyPresent) {
+            parsedPackage.addUsesLibrary(0, libraryName);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/parsing/library/SharedLibraryNames.java
similarity index 91%
rename from core/java/android/content/pm/SharedLibraryNames.java
rename to core/java/android/content/pm/parsing/library/SharedLibraryNames.java
index a607a9f..7b691c0 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/parsing/library/SharedLibraryNames.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.content.pm;
+package android.content.pm.parsing.library;
 
 /**
  * A set of shared library names
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 9af9eda..45a9cf5 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -295,6 +295,10 @@
     /** {@hide} */
     @NonNull
     public String getTraceName(@NonNull Message message) {
+        if (message.callback instanceof TraceNameSupplier) {
+            return ((TraceNameSupplier) message.callback).getTraceName();
+        }
+
         final StringBuilder sb = new StringBuilder();
         sb.append(getClass().getName()).append(": ");
         if (message.callback != null) {
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/core/java/android/os/TraceNameSupplier.java
similarity index 65%
copy from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
copy to core/java/android/os/TraceNameSupplier.java
index 10d0551..e4b3a4e 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
+++ b/core/java/android/os/TraceNameSupplier.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,13 +12,20 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
+package android.os;
 
-package com.android.frameworks.coretests;
+import android.annotation.NonNull;
 
-import android.app.Activity;
+/**
+ * Supplier for custom trace messages.
+ *
+ * @hide
+ */
+public interface TraceNameSupplier {
 
-public class TestActivity extends Activity {
-
+    /**
+     * Gets the name used for trace messages.
+     */
+    @NonNull String getTraceName();
 }
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index a5b71f6..59fb3d9 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -86,12 +86,22 @@
     public abstract void setDeviceManaged(boolean isManaged);
 
     /**
+     * Returns whether the device is managed by device owner.
+     */
+    public abstract boolean isDeviceManaged();
+
+    /**
      * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
      * whether the user is managed by profile owner.
      */
     public abstract void setUserManaged(int userId, boolean isManaged);
 
     /**
+     * whether a profile owner manages this user.
+     */
+    public abstract boolean isUserManaged(int userId);
+
+    /**
      * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to omit
      * restriction check, because DevicePolicyManager must always be able to set user icon
      * regardless of any restriction.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5b9205d..351462f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -153,6 +153,11 @@
     public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
     /** {@hide} */
     public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
+    /** {@hide} */
+    public static final String PROP_FUSE = "persist.sys.fuse";
+    /** {@hide} */
+    public static final String PROP_FUSE_SNAPSHOT = "sys.fuse_snapshot";
+
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2b3a2ab..4dd9bab 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -151,7 +151,7 @@
                 .PRIORITY_CATEGORY_ALARMS) != 0;
         mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_MEDIA) != 0;
-        mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+        mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
                 mNotificationPolicy);
         mStreamType = streamType;
         mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
@@ -571,7 +571,7 @@
                         .PRIORITY_CATEGORY_ALARMS) != 0;
                 mAllowMedia = (mNotificationPolicy.priorityCategories
                         & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0;
-                mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+                mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
                         mNotificationPolicy);
                 updateSlider();
             }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 5e201e4..fd1381a 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -257,10 +257,20 @@
     /**
      * Namespace for storage-related features.
      *
+     * @deprecated Replace storage namespace with storage_native_boot.
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public static final String NAMESPACE_STORAGE = "storage";
+
+    /**
+     * Namespace for storage-related features, including native and boot.
+     *
      * @hide
      */
     @SystemApi
-    public static final String NAMESPACE_STORAGE = "storage";
+    public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
 
     /**
      * Namespace for System UI related features.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 61d8eb1..3c26df3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7631,6 +7631,19 @@
                 "face_unlock_always_require_confirmation";
 
         /**
+         * Whether or not a user should re enroll their face.
+         *
+         * Face unlock re enroll.
+         *  0 = No re enrollment.
+         *  1 = Re enrollment is suggested.
+         *  2 = Re enrollment is required after a set time period.
+         *  3 = Re enrollment is required immediately.
+         *
+         * @hide
+         */
+        public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll";
+
+        /**
          * Whether or not debugging is enabled.
          * @hide
          */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 937990f7..1f2c872 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1904,10 +1904,10 @@
     }
 
     /**
-     * Determines whether dnd behavior should mute all notification/ringer sounds
-     * (sounds associated with ringer volume discluding system)
+     * Determines whether dnd behavior should mute all ringer-controlled sounds
+     * This includes notification, ringer and system sounds
      */
-    public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
+    public static boolean areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy
             policy) {
         boolean allowReminders = (policy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
@@ -1920,20 +1920,19 @@
         boolean allowRepeatCallers = (policy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
         boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+        boolean allowSystem =  (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
         return !allowReminders && !allowCalls && !allowMessages && !allowEvents
-                && !allowRepeatCallers && !areChannelsBypassingDnd;
+                && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem;
     }
 
     /**
-     * Determines whether dnd behavior should mute all sounds controlled by ringer
+     * Determines whether dnd behavior should mute all sounds
      */
     public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy
             policy) {
         boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
         boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
-        boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
-        return !allowAlarms && !allowMedia && !allowSystem
-                && areAllPriorityOnlyNotificationZenSoundsMuted(policy);
+        return !allowAlarms && !allowMedia && areAllPriorityOnlyRingerSoundsMuted(policy);
     }
 
     /**
@@ -1943,24 +1942,25 @@
         return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
                 || zen == Global.ZEN_MODE_ALARMS
                 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(consolidatedPolicy));
+                && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy));
     }
 
     /**
-     * Determines whether dnd behavior should mute all sounds controlled by ringer
+     * Determines whether dnd behavior should mute all ringer-controlled sounds
+     * This includes notification, ringer and system sounds
      */
-    public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
+    public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) {
         return !config.allowReminders && !config.allowCalls && !config.allowMessages
                 && !config.allowEvents && !config.allowRepeatCallers
-                && !config.areChannelsBypassingDnd;
+                && !config.areChannelsBypassingDnd && !config.allowSystem;
     }
 
     /**
-     * Determines whether all dnd mutes all sounds
+     * Determines whether dnd mutes all sounds
      */
     public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
-        return !config.allowAlarms  && !config.allowMedia && !config.allowSystem
-                && areAllPriorityOnlyNotificationZenSoundsMuted(config);
+        return !config.allowAlarms  && !config.allowMedia
+                && areAllPriorityOnlyRingerSoundsMuted(config);
     }
 
     /**
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 5143f18..2470d19 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -58,8 +58,8 @@
  * Abstract base class for the TextClassifier service.
  *
  * <p>A TextClassifier service provides text classification related features for the system.
- * The system's default TextClassifierService is configured in
- * {@code config_defaultTextClassifierService}. If this config has no value, a
+ * The system's default TextClassifierService provider is configured in
+ * {@code config_defaultTextClassifierPackage}. If this config has no value, a
  * {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
  *
  * <p>See: {@link TextClassifier}.
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index e72ff38..d47604d 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -37,7 +37,7 @@
  * View.startDragAndDrop} by the app that started the drag operation.
  * </p>
  * <p>
- * The life cycle of the permissions is bound to the activity used to call {@link
+ * The lifecycle of the permissions is bound to the activity used to call {@link
  * android.app.Activity#requestDragAndDropPermissions(DragEvent) requestDragAndDropPermissions}. The
  * permissions are revoked when this activity is destroyed, or when {@link #release()} is called,
  * whichever occurs first.
@@ -49,6 +49,10 @@
  * {@link Activity#onSaveInstanceState} bundle and later retrieved in order to manually release
  * the permissions once they are no longer needed.
  * </p>
+ * <p>
+ * Learn more about <a href="/guide/topics/ui/drag-drop#DragPermissionsMultiWindow">drag permissions
+ * in multi-window mode</a>.
+ * </p>
  */
 public final class DragAndDropPermissions implements Parcelable {
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d5559aa..6639fbf 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -187,8 +187,7 @@
 
     private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
             InputWindowHandle handle);
-    private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken,
-            IBinder toToken);
+
     private static native boolean nativeGetProtectedContentSupport();
     private static native void nativeSetMetadata(long transactionObj, long nativeObject, int key,
             Parcel data);
@@ -2239,22 +2238,6 @@
         }
 
         /**
-         * Transfers touch focus from one window to another. It is possible for multiple windows to
-         * have touch focus if they support split touch dispatch
-         * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
-         * method only transfers touch focus of the specified window without affecting
-         * other windows that may also have touch focus at the same time.
-         * @param fromToken The token of a window that currently has touch focus.
-         * @param toToken The token of the window that should receive touch focus in
-         * place of the first.
-         * @hide
-         */
-        public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
-            nativeTransferTouchFocus(mNativeObject, fromToken, toToken);
-            return this;
-        }
-
-        /**
          * Waits until any changes to input windows have been sent from SurfaceFlinger to
          * InputFlinger before returning.
          *
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 5f3ce33..606e8f9 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -230,6 +230,8 @@
     /**
      * Sets the identifier used to set the hint associated with this view.
      *
+     * <p>Used as metadata for fingerprinting view nodes/structures.
+     *
      * <p>Should only be set when the node is used for autofill purposes - it will be ignored
      * when used for Assist.
      */
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 18d4d69..7282008 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -193,26 +193,24 @@
     }
 
     /**
-     * Used with {@link #setMixedContentMode}
-     *
      * In this mode, the WebView will allow a secure origin to load content from any other origin,
      * even if that origin is insecure. This is the least secure mode of operation for the WebView,
      * and where possible apps should not set this mode.
+     *
+     * @see #setMixedContentMode
      */
     public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0;
 
     /**
-     * Used with {@link #setMixedContentMode}
-     *
      * In this mode, the WebView will not allow a secure origin to load content from an insecure
      * origin. This is the preferred and most secure mode of operation for the WebView and apps are
      * strongly advised to use this mode.
+     *
+     * @see #setMixedContentMode
      */
     public static final int MIXED_CONTENT_NEVER_ALLOW = 1;
 
     /**
-     * Used with {@link #setMixedContentMode}
-     *
      * In this mode, the WebView will attempt to be compatible with the approach of a modern web
      * browser with regard to mixed content. Some insecure content may be allowed to be loaded by
      * a secure origin and other types of content will be blocked. The types of content are allowed
@@ -221,6 +219,8 @@
      * This mode is intended to be used by apps that are not in control of the content that they
      * render but desire to operate in a reasonably secure environment. For highest security, apps
      * are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}.
+     *
+     * @see #setMixedContentMode
      */
     public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
 
@@ -234,30 +234,30 @@
     public @interface ForceDark {}
 
     /**
-     * Used with {@link #setForceDark}
-     *
      * Disable force dark, irrespective of the force dark mode of the WebView parent. In this mode,
      * WebView content will always be rendered as-is, regardless of whether native views are being
      * automatically darkened.
+     *
+     * @see #setForceDark
      */
     public static final int FORCE_DARK_OFF = 0;
 
     /**
-     * Used with {@link #setForceDark}
-     *
      * Enable force dark dependent on the state of the WebView parent view. If the WebView parent
      * view is being automatically force darkened
      * (see: {@link android.view.View#setForceDarkAllowed}), then WebView content will be rendered
      * so as to emulate a dark theme. WebViews that are not attached to the view hierarchy will not
      * be inverted.
+     *
+     * @see #setForceDark
      */
     public static final int FORCE_DARK_AUTO = 1;
 
     /**
-     * Used with {@link #setForceDark}
-     *
      * Unconditionally enable force dark. In this mode WebView content will always be rendered so
      * as to emulate a dark theme.
+     *
+     * @see #setForceDark
      */
     public static final int FORCE_DARK_ON = 2;
 
@@ -1471,6 +1471,7 @@
      * Set the force dark mode for this WebView.
      *
      * @param forceDark the force dark mode to set.
+     * @see #getForceDark
      */
     public void setForceDark(@ForceDark int forceDark) {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1478,10 +1479,10 @@
 
     /**
      * Get the force dark mode for this WebView.
-     *
-     * The default force dark mode is {@link #FORCE_DARK_AUTO}
+     * The default force dark mode is {@link #FORCE_DARK_AUTO}.
      *
      * @return the currently set force dark mode.
+     * @see #setForceDark
      */
     public @ForceDark int getForceDark() {
         // Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1516,34 +1517,34 @@
     public abstract @MenuItemFlags int getDisabledActionModeMenuItems();
 
     /**
-     * Used with {@link #setDisabledActionModeMenuItems}.
-     *
      * No menu items should be disabled.
+     *
+     * @see #setDisabledActionModeMenuItems
      */
     public static final int MENU_ITEM_NONE = 0;
 
     /**
-     * Used with {@link #setDisabledActionModeMenuItems}.
-     *
      * Disable menu item "Share".
+     *
+     * @see #setDisabledActionModeMenuItems
      */
     public static final int MENU_ITEM_SHARE = 1 << 0;
 
     /**
-     * Used with {@link #setDisabledActionModeMenuItems}.
-     *
      * Disable menu item "Web Search".
+     *
+     * @see #setDisabledActionModeMenuItems
      */
     public static final int MENU_ITEM_WEB_SEARCH = 1 << 1;
 
     /**
-     * Used with {@link #setDisabledActionModeMenuItems}.
-     *
      * Disable all the action mode menu items for text processing.
      * By default WebView searches for activities that are able to handle
      * {@link android.content.Intent#ACTION_PROCESS_TEXT} and show them in the
      * action mode menu. If this flag is set via {@link
      * #setDisabledActionModeMenuItems}, these menu items will be disabled.
+     *
+     * @see #setDisabledActionModeMenuItems
      */
     public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2;
 }
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 80de6fc..562cc4f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -1103,6 +1103,7 @@
             }
 
             node.setEnabled(isDayEnabled);
+            node.setClickable(true);
 
             if (virtualViewId == mActivatedDay) {
                 // TODO: This should use activated once that's supported.
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 5ea970d..8283eb7 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -77,10 +77,10 @@
      * @param state    of the reported change - enabled/disabled/only logged
      */
     public void reportChange(int uid, long changeId, int state) {
-        debugLog(uid, changeId, state);
         ChangeReport report = new ChangeReport(uid, changeId, state);
         synchronized (mReportedChanges) {
             if (!mReportedChanges.contains(report)) {
+                debugLog(uid, changeId, state);
                 StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
                         state, mSource);
                 mReportedChanges.add(report);
@@ -89,7 +89,6 @@
     }
 
     private void debugLog(int uid, long changeId, int state) {
-        //TODO(b/138374585): Implement rate limiting for the logs.
         String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
                 uid, stateToString(state));
         if (mSource == StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index fee8345..f04a671 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -28,12 +28,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
 import android.os.SELinux;
-import android.os.SystemProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Slog;
@@ -88,11 +87,12 @@
             }
         }
 
-        public static Handle create(Package pkg) throws IOException {
-            return create(pkg.getAllCodePaths(),
-                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
-                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
-                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+        public static Handle create(AndroidPackage pkg) throws IOException {
+            return create(
+                    pkg.makeListAllCodePaths(),
+                    (pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) != 0,
+                    (pkg.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
+                    (pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
         }
 
         public static Handle create(PackageLite lite) throws IOException {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index d6caa09..9fff447 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
+import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
 import android.os.DeadObjectException;
 import android.os.Debug;
@@ -33,6 +34,9 @@
 import com.android.server.NetworkManagementSocketTagger;
 import dalvik.system.RuntimeHooks;
 import dalvik.system.VMRuntime;
+
+import libcore.net.MimeMap;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -199,6 +203,13 @@
     public static void preForkInit() {
         if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
         RuntimeInit.enableDdms();
+        /*
+         * Replace libcore's minimal default mapping between MIME types and file
+         * extensions with a mapping that's suitable for Android. Android's mapping
+         * contains many more entries that are derived from IANA registrations but
+         * with several customizations (extensions, overrides).
+         */
+        MimeMap.setDefault(DefaultMimeMapFactory.create());
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 821022f..bc80197 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -600,6 +600,14 @@
         return cur;
     }
 
+    public static @NonNull <T> ArrayList<T> add(@Nullable ArrayList<T> cur, int index, T val) {
+        if (cur == null) {
+            cur = new ArrayList<>();
+        }
+        cur.add(index, val);
+        return cur;
+    }
+
     public static @Nullable <T> ArrayList<T> remove(@Nullable ArrayList<T> cur, T val) {
         if (cur == null) {
             return null;
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1fdb1f3..e12c031 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.util.function.pooled;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Message;
 import android.text.TextUtils;
@@ -527,6 +528,36 @@
         return r;
     }
 
+    // TODO: add unit test
+    @NonNull
+    private static String getFriendlyName(@NonNull Object function) {
+        // Full function has one of the following formats:
+        //   package-$$Lambda$class$randomId
+        //   package-$$Lambda$randomId
+        //
+        // We just want just package.class$Lambda (or package$Lambda) respectively
+
+        final String fullFunction = function.toString();
+
+        final int endPkgIdx = fullFunction.indexOf("-$$");
+        if (endPkgIdx == -1) return fullFunction;
+
+        // firstDollarIdx could be either beginning of class or beginning of the random id
+        final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3);
+        if (firstDollarIdx == -1) return fullFunction;
+
+        final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1);
+        if (endClassIdx == -1) {
+            // Just package
+            return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda";
+        }
+
+        // Package + class
+        return fullFunction.substring(0, endPkgIdx)
+                + fullFunction.substring(firstDollarIdx + 1, endClassIdx)
+                + "$Lambda";
+    }
+
     private static void setIfInBounds(Object[] array, int i, Object a) {
         if (i < ArrayUtils.size(array)) array[i] = a;
     }
@@ -566,6 +597,11 @@
         return this;
     }
 
+    @Override
+    public String getTraceName() {
+        return getFriendlyName(mFunc);
+    }
+
     private boolean isRecycled() {
         return (mFlags & FLAG_RECYCLED) != 0;
     }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
index 89ca82e..f0bc2ca 100644
--- a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.util.function.pooled;
 
+import android.os.TraceNameSupplier;
+
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 
 /**
@@ -24,7 +26,8 @@
  * @see PooledLambda
  * @hide
  */
-public interface PooledRunnable extends PooledLambda, Runnable, ThrowingRunnable {
+public interface PooledRunnable
+        extends PooledLambda, Runnable, ThrowingRunnable, TraceNameSupplier {
     /** @inheritDoc */
     PooledRunnable recycleOnUse();
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5a0f16e..e3b8560 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -54,6 +54,8 @@
 
     whole_static_libs: ["libandroid_graphics"],
 
+    export_static_lib_headers: ["libandroid_graphics"],
+
     shared_libs: [
         "libbase",
         "libcutils",
@@ -337,7 +339,9 @@
     cppflags: ["-Wno-conversion-null"],
 
     srcs: [
+        "android/graphics/apex/android_bitmap.cpp",
         "android/graphics/apex/android_region.cpp",
+        "android/graphics/apex/android_paint.cpp",
 
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
@@ -390,7 +394,6 @@
     ],
 
     export_include_dirs: [
-        ".",
         "android/graphics/apex/include",
     ],
 
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index 06e31a1..59adbb2 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -39,8 +39,6 @@
             jobject ninePatchInsets = nullptr, int density = -1);
 
 
-void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap);
-
 Bitmap& toBitmap(JNIEnv* env, jobject bitmap);
 Bitmap& toBitmap(jlong bitmapHandle);
 
diff --git a/core/jni/android/graphics/apex/TypeCast.h b/core/jni/android/graphics/apex/TypeCast.h
new file mode 100644
index 0000000..96721d0
--- /dev/null
+++ b/core/jni/android/graphics/apex/TypeCast.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GRAPHICS_TYPECAST_H
+#define ANDROID_GRAPHICS_TYPECAST_H
+
+struct ABitmap;
+struct ACanvas;
+struct APaint;
+
+namespace android {
+
+    class Bitmap;
+    class Canvas;
+    class Paint;
+
+    class TypeCast {
+    public:
+        static inline Bitmap& toBitmapRef(const ABitmap* bitmap) {
+            return const_cast<Bitmap&>(reinterpret_cast<const Bitmap&>(*bitmap));
+        }
+
+        static inline Bitmap* toBitmap(ABitmap* bitmap) {
+            return reinterpret_cast<Bitmap*>(bitmap);
+        }
+
+        static inline ABitmap* toABitmap(Bitmap* bitmap) {
+            return reinterpret_cast<ABitmap*>(bitmap);
+        }
+
+        static inline Canvas* toCanvas(ACanvas* canvas) {
+            return reinterpret_cast<Canvas*>(canvas);
+        }
+
+        static inline ACanvas* toACanvas(Canvas* canvas) {
+            return reinterpret_cast<ACanvas *>(canvas);
+        }
+
+        static inline const Paint& toPaintRef(const APaint* paint) {
+            return reinterpret_cast<const Paint&>(*paint);
+        }
+
+        static inline const Paint* toPaint(const APaint* paint) {
+            return reinterpret_cast<const Paint*>(paint);
+        }
+
+        static inline Paint* toPaint(APaint* paint) {
+            return reinterpret_cast<Paint*>(paint);
+        }
+
+        static inline APaint* toAPaint(Paint* paint) {
+            return reinterpret_cast<APaint*>(paint);
+        }
+    };
+}; // namespace android
+
+#endif // ANDROID_GRAPHICS_TYPECAST_H
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
new file mode 100644
index 0000000..96cc5db
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/bitmap.h"
+#include "Bitmap.h"
+#include "TypeCast.h"
+
+#include <hwui/Bitmap.h>
+
+using namespace android;
+
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) {
+    Bitmap& bitmap = android::bitmap::toBitmap(env, bitmapObj);
+    bitmap.ref();
+    return TypeCast::toABitmap(&bitmap);
+}
+
+void ABitmap_acquireRef(ABitmap* bitmap) {
+    SkSafeRef(TypeCast::toBitmap(bitmap));
+}
+
+void ABitmap_releaseRef(ABitmap* bitmap) {
+    SkSafeUnref(TypeCast::toBitmap(bitmap));
+}
+
+static AndroidBitmapFormat getFormat(Bitmap* bitmap) {
+    switch (bitmap->colorType()) {
+        case kN32_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_8888;
+        case kRGB_565_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGB_565;
+        case kARGB_4444_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_4444;
+        case kAlpha_8_SkColorType:
+            return ANDROID_BITMAP_FORMAT_A_8;
+        case kRGBA_F16_SkColorType:
+            return ANDROID_BITMAP_FORMAT_RGBA_F16;
+        default:
+            return ANDROID_BITMAP_FORMAT_NONE;
+    }
+}
+
+static SkColorType getColorType(AndroidBitmapFormat format) {
+    switch (format) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
+            return kN32_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGB_565:
+            return kRGB_565_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
+            return kARGB_4444_SkColorType;
+        case ANDROID_BITMAP_FORMAT_A_8:
+            return kAlpha_8_SkColorType;
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
+            return kRGBA_F16_SkColorType;
+        default:
+            return kUnknown_SkColorType;
+    }
+}
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
+    SkColorType dstColorType = getColorType(dstFormat);
+    if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
+        SkBitmap srcBitmap;
+        TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap);
+
+        sk_sp<Bitmap> dstBitmap =
+                Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType));
+        if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(),
+                                              dstBitmap->rowBytes(), 0, 0)) {
+            return TypeCast::toABitmap(dstBitmap.release());
+        }
+    }
+    return nullptr;
+}
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+
+    AndroidBitmapInfo info;
+    info.width = bitmap->width();
+    info.height = bitmap->height();
+    info.stride = bitmap->rowBytes();
+    info.format = getFormat(bitmap);
+    return info;
+}
+
+void* ABitmap_getPixels(ABitmap* bitmapHandle) {
+    Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+    if (bitmap->isHardware()) {
+        return nullptr;
+    }
+    return bitmap->pixels();
+}
diff --git a/core/jni/android/graphics/apex/android_canvas.cpp b/core/jni/android/graphics/apex/android_canvas.cpp
index 7a4495f..527a745 100644
--- a/core/jni/android/graphics/apex/android_canvas.cpp
+++ b/core/jni/android/graphics/apex/android_canvas.cpp
@@ -16,6 +16,7 @@
 
 #include "android/graphics/canvas.h"
 
+#include "TypeCast.h"
 #include "GraphicsJNI.h"
 
 #include <hwui/Canvas.h>
@@ -25,14 +26,6 @@
 
 using namespace android;
 
-static inline Canvas* toCanvas(ACanvas* aCanvas) {
-    return reinterpret_cast<Canvas*>(aCanvas);
-}
-
-static inline ACanvas* toACanvas(Canvas* canvas) {
-    return reinterpret_cast<ACanvas*>(canvas);
-}
-
 bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
     ANativeWindow_Buffer buffer { 0, 0, 0, bufferFormat, nullptr, {0} };
     const SkColorType colorType = uirenderer::ANativeWindowToImageInfo(buffer, nullptr).colorType();
@@ -40,11 +33,11 @@
 }
 
 ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
-    return toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
+    return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
 }
 
-void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
-                       int32_t /*android_dataspace_t*/ dataspace) {
+static SkBitmap convert(const ANativeWindow_Buffer* buffer,
+                        int32_t /*android_dataspace_t*/ dataspace) {
     SkBitmap bitmap;
     if (buffer != nullptr && buffer->width > 0 && buffer->height > 0) {
         sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace));
@@ -53,18 +46,44 @@
         bitmap.setInfo(imageInfo, rowBytes);
         bitmap.setPixels(buffer->bits);
     }
-
-    toCanvas(canvas)->setBitmap(bitmap);
+    return bitmap;
 }
 
-void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
-    //TODO update Canvas to take antialias param
-    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-                               SkClipOp::kIntersect);
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+                              int32_t /*android_dataspace_t*/ dataspace) {
+    return TypeCast::toACanvas(Canvas::create_canvas(convert(buffer, dataspace)));
 }
 
-void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+void ACanvas_destroyCanvas(ACanvas* canvas) {
+    delete TypeCast::toCanvas(canvas);
+}
+
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+
+
+    TypeCast::toCanvas(canvas)->setBitmap(convert(buffer, dataspace));
+}
+
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
     //TODO update Canvas to take antialias param
-    toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-                               SkClipOp::kDifference);
+    TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+                                         clipRect->bottom, SkClipOp::kIntersect);
+}
+
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
+    //TODO update Canvas to take antialias param
+    TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+                                         clipRect->bottom, SkClipOp::kDifference);
+}
+
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint) {
+    TypeCast::toCanvas(canvas)->drawRect(rect->left, rect->top, rect->right, rect->bottom,
+                                         TypeCast::toPaintRef(paint));
+}
+
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+                        const APaint* paint) {
+    TypeCast::toCanvas(canvas)->drawBitmap(TypeCast::toBitmapRef(bitmap), left, top,
+                                           TypeCast::toPaint(paint));
 }
diff --git a/core/jni/android/graphics/apex/android_paint.cpp b/core/jni/android/graphics/apex/android_paint.cpp
new file mode 100644
index 0000000..70bd085
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_paint.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/paint.h"
+
+#include "TypeCast.h"
+
+#include <hwui/Paint.h>
+
+using namespace android;
+
+
+APaint* APaint_createPaint() {
+    return TypeCast::toAPaint(new Paint());
+}
+
+void APaint_destroyPaint(APaint* paint) {
+    delete TypeCast::toPaint(paint);
+}
+
+static SkBlendMode convertBlendMode(ABlendMode blendMode) {
+    switch (blendMode) {
+        case ABLEND_MODE_CLEAR:
+            return SkBlendMode::kClear;
+        case ABLEND_MODE_SRC_OVER:
+            return SkBlendMode::kSrcOver;
+        case ABLEND_MODE_SRC:
+            return SkBlendMode::kSrc;
+    }
+}
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) {
+    TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode));
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
new file mode 100644
index 0000000..bfa4c8d
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_BITMAP_H
+#define ANDROID_GRAPHICS_BITMAP_H
+
+#include <android/bitmap.h>
+#include <jni.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics bitmap.
+ */
+typedef struct ABitmap ABitmap;
+
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj);
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat);
+
+void ABitmap_acquireRef(ABitmap* bitmap);
+void ABitmap_releaseRef(ABitmap* bitmap);
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap);
+
+void* ABitmap_getPixels(ABitmap* bitmap);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Bitmap {
+    public:
+        Bitmap() : mBitmap(nullptr) {}
+        Bitmap(JNIEnv* env, jobject bitmapObj) :
+                mBitmap(ABitmap_acquireBitmapFromJava(env, bitmapObj)) {}
+        Bitmap(const Bitmap& src) : mBitmap(src.mBitmap) { ABitmap_acquireRef(src.mBitmap); }
+        ~Bitmap() { ABitmap_releaseRef(mBitmap); }
+
+        // copy operator
+        Bitmap& operator=(const Bitmap& other) {
+            if (&other != this) {
+                ABitmap_releaseRef(mBitmap);
+                mBitmap = other.mBitmap;
+                ABitmap_acquireRef(mBitmap);
+            }
+            return *this;
+        }
+
+        // move operator
+        Bitmap& operator=(Bitmap&& other) {
+            if (&other != this) {
+                ABitmap_releaseRef(mBitmap);
+                mBitmap = other.mBitmap;
+                other.mBitmap = nullptr;
+            }
+            return *this;
+        }
+
+        Bitmap copy(AndroidBitmapFormat dstFormat) const {
+            return Bitmap(ABitmap_copy(mBitmap, dstFormat));
+        }
+
+        bool isValid() const { return mBitmap != nullptr; }
+        bool isEmpty() const {
+            AndroidBitmapInfo info = getInfo();
+            return info.width <= 0 || info.height <= 0;
+        }
+        void reset() {
+            ABitmap_releaseRef(mBitmap);
+            mBitmap = nullptr;
+        }
+
+        const ABitmap* get() const { return mBitmap; }
+
+        AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); }
+        void* getPixels() const { return ABitmap_getPixels(mBitmap); }
+    private:
+        // takes ownership of the provided ABitmap
+        Bitmap(ABitmap* bitmap) : mBitmap(bitmap) {}
+
+        ABitmap* mBitmap;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+#endif // ANDROID_GRAPHICS_BITMAP_H
\ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/canvas.h b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
index c35a7d6..190aba4 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/canvas.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
@@ -16,6 +16,8 @@
 #ifndef ANDROID_GRAPHICS_CANVAS_H
 #define ANDROID_GRAPHICS_CANVAS_H
 
+#include <android/graphics/bitmap.h>
+#include <android/graphics/paint.h>
 #include <android/native_window.h>
 #include <android/rect.h>
 #include <jni.h>
@@ -23,8 +25,8 @@
 __BEGIN_DECLS
 
 /**
-* Opaque handle for a native graphics canvas.
-*/
+ * Opaque handle for a native graphics canvas.
+ */
 typedef struct ACanvas ACanvas;
 
 //  One of AHardwareBuffer_Format.
@@ -33,34 +35,104 @@
 /**
  * Returns a native handle to a Java android.graphics.Canvas
  *
- * @param env
- * @param canvas
  * @return ACanvas* that is only valid for the life of the jobject.
  */
 ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas);
 
 /**
+ * Creates a canvas that wraps the buffer
+ *
+ * @param buffer required
+ */
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+                              int32_t /*android_dataspace_t*/ dataspace);
+
+void ACanvas_destroyCanvas(ACanvas* canvas);
+
+/**
  * Updates the canvas to render into the pixels in the provided buffer
  *
- * @param canvas
  * @param buffer The buffer that will provide the backing store for this canvas.  The buffer must
  *               remain valid until the this method is called again with either another active
  *               buffer or nullptr.  If nullptr is given the canvas will release the previous buffer
  *               and set an empty backing store.
- * @param dataspace
  */
 void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
                        int32_t /*android_dataspace_t*/ dataspace);
 
 /**
  * Clips operations on the canvas to the intersection of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
  */
-void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
 
 /**
  * Clips operations on the canvas to the difference of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
  */
-void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
+
+/**
+ *
+ * @param rect required
+ * @param paint required
+ */
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint);
+
+/**
+ *
+ * @param bitmap required
+ * @param left
+ * @param top
+ * @param paint
+ */
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+                        const APaint* paint);
 
 __END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Canvas {
+    public:
+        Canvas(JNIEnv* env, jobject canvasObj) :
+                mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)),
+                mOwnedPtr(false) {}
+        Canvas(const ANativeWindow_Buffer& buffer, int32_t /*android_dataspace_t*/ dataspace) :
+                mCanvas(ACanvas_createCanvas(&buffer, dataspace)),
+                mOwnedPtr(true) {}
+        ~Canvas() {
+            if (mOwnedPtr) {
+                ACanvas_destroyCanvas(mCanvas);
+            }
+        }
+
+        void setBuffer(const ANativeWindow_Buffer* buffer,
+                       int32_t /*android_dataspace_t*/ dataspace) {
+            ACanvas_setBuffer(mCanvas, buffer, dataspace);
+        }
+
+        void clipRect(const ARect& clipRect, bool doAntiAlias = false) {
+            ACanvas_clipRect(mCanvas, &clipRect, doAntiAlias);
+        }
+
+        void drawRect(const ARect& rect, const Paint& paint) {
+            ACanvas_drawRect(mCanvas, &rect, &paint.get());
+        }
+        void drawBitmap(const Bitmap& bitmap, float left, float top, const Paint* paint) {
+            const APaint* aPaint = (paint) ? &paint->get() : nullptr;
+            ACanvas_drawBitmap(mCanvas, bitmap.get(), left, top, aPaint);
+        }
+
+    private:
+        ACanvas* mCanvas;
+        const bool mOwnedPtr;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
 #endif // ANDROID_GRAPHICS_CANVAS_H
\ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/paint.h b/core/jni/android/graphics/apex/include/android/graphics/paint.h
new file mode 100644
index 0000000..5895e00
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/paint.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_PAINT_H
+#define ANDROID_GRAPHICS_PAINT_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics canvas.
+ */
+typedef struct APaint APaint;
+
+/** Bitmap pixel format. */
+enum ABlendMode {
+    /** replaces destination with zero: fully transparent */
+    ABLEND_MODE_CLEAR    = 0,
+    /** source over destination */
+    ABLEND_MODE_SRC_OVER = 1,
+    /** replaces destination **/
+    ABLEND_MODE_SRC      = 2,
+};
+
+APaint* APaint_createPaint();
+
+void APaint_destroyPaint(APaint* paint);
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class Paint {
+    public:
+        Paint() : mPaint(APaint_createPaint()) {}
+        ~Paint() { APaint_destroyPaint(mPaint); }
+
+        void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); }
+
+        const APaint& get() const { return *mPaint; }
+
+    private:
+        APaint* mPaint;
+    };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+
+#endif // ANDROID_GRAPHICS_PAINT_H
\ No newline at end of file
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 58c5871..82601ba 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -24,15 +24,13 @@
 #include <assert.h>
 #include <dlfcn.h>
 
+#include <android/graphics/bitmap.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
 #include <ETC1/etc1.h>
 
-#include <SkBitmap.h>
-
 #include "core_jni_helpers.h"
-#include "android/graphics/Bitmap.h"
 
 #undef LOG_TAG
 #define LOG_TAG "OpenGLUtil"
@@ -628,31 +626,27 @@
 
 // The internal format is no longer the same as pixel format, per Table 2 in
 // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
-static int checkInternalFormat(SkColorType colorType, int internalformat,
-    int type)
+static bool checkInternalFormat(int32_t bitmapFormat, int internalformat, int type)
 {
-    switch(colorType) {
-        case kN32_SkColorType:
-            return (type == GL_UNSIGNED_BYTE &&
-                    internalformat == GL_RGBA) ||
-                (type == GL_UNSIGNED_BYTE &&
-                 internalformat == GL_SRGB8_ALPHA8) ? 0 : -1;
-        case kAlpha_8_SkColorType:
-            return (type == GL_UNSIGNED_BYTE &&
-                internalformat == GL_ALPHA) ? 0 : -1;
-        case kARGB_4444_SkColorType:
-            return (type == GL_UNSIGNED_SHORT_4_4_4_4 &&
-                internalformat == GL_RGBA) ? 0 : -1;
-        case kRGB_565_SkColorType:
-            return (type == GL_UNSIGNED_SHORT_5_6_5 &&
-                internalformat == GL_RGB) ? 0 : -1;
-        case kRGBA_F16_SkColorType:
-            return (type == GL_HALF_FLOAT &&
-                internalformat == GL_RGBA16F) ? 0 : -1;
+    if (internalformat == GL_PALETTE8_RGBA8_OES) {
+        return false;
+    }
+    switch(bitmapFormat) {
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
+            return (type == GL_UNSIGNED_BYTE && internalformat == GL_RGBA) ||
+                   (type == GL_UNSIGNED_BYTE && internalformat == GL_SRGB8_ALPHA8);
+        case ANDROID_BITMAP_FORMAT_A_8:
+            return (type == GL_UNSIGNED_BYTE && internalformat == GL_ALPHA);
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
+            return (type == GL_UNSIGNED_SHORT_4_4_4_4 && internalformat == GL_RGBA);
+        case ANDROID_BITMAP_FORMAT_RGB_565:
+            return (type == GL_UNSIGNED_SHORT_5_6_5 && internalformat == GL_RGB);
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
+            return (type == GL_HALF_FLOAT && internalformat == GL_RGBA16F);
         default:
             break;
     }
-    return -1;
+    return false;
 }
 
 // The internal format is no longer the same as pixel format, per Table 2 in
@@ -670,107 +664,92 @@
     }
 }
 
-static int getInternalFormat(SkColorType colorType)
-{
-    switch(colorType) {
-        case kAlpha_8_SkColorType:
+static int getInternalFormat(int32_t bitmapFormat) {
+    switch(bitmapFormat) {
+        case ANDROID_BITMAP_FORMAT_A_8:
             return GL_ALPHA;
-        case kARGB_4444_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
             return GL_RGBA;
-        case kN32_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
             return GL_RGBA;
-        case kRGB_565_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGB_565:
             return GL_RGB;
-        case kRGBA_F16_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
             return GL_RGBA16F;
         default:
             return -1;
     }
 }
 
-static int getType(SkColorType colorType)
-{
-    switch(colorType) {
-        case kAlpha_8_SkColorType:
+static int getType(int32_t bitmapFormat) {
+    switch(bitmapFormat) {
+        case ANDROID_BITMAP_FORMAT_A_8:
             return GL_UNSIGNED_BYTE;
-        case kARGB_4444_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_4444:
             return GL_UNSIGNED_SHORT_4_4_4_4;
-        case kN32_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_8888:
             return GL_UNSIGNED_BYTE;
-        case kRGB_565_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGB_565:
             return GL_UNSIGNED_SHORT_5_6_5;
-        case kRGBA_F16_SkColorType:
+        case ANDROID_BITMAP_FORMAT_RGBA_F16:
             return GL_HALF_FLOAT;
         default:
             return -1;
     }
 }
 
-static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
-        jlong bitmapPtr)
+static jint util_getInternalFormat(JNIEnv *env, jclass clazz, jobject bitmapObj)
 {
-    SkBitmap nativeBitmap;
-    bitmap::toSkBitmap(bitmapPtr, &nativeBitmap);
-    return getInternalFormat(nativeBitmap.colorType());
+    graphics::Bitmap bitmap(env, bitmapObj);
+    return getInternalFormat(bitmap.getInfo().format);
 }
 
-static jint util_getType(JNIEnv *env, jclass clazz,
-        jlong bitmapPtr)
+static jint util_getType(JNIEnv *env, jclass clazz, jobject bitmapObj)
 {
-    SkBitmap nativeBitmap;
-    bitmap::toSkBitmap(bitmapPtr, &nativeBitmap);
-    return getType(nativeBitmap.colorType());
+    graphics::Bitmap bitmap(env, bitmapObj);
+    return getType(bitmap.getInfo().format);
 }
 
-static jint util_texImage2D(JNIEnv *env, jclass clazz,
-        jint target, jint level, jint internalformat,
-        jlong bitmapPtr, jint type, jint border)
+static jint util_texImage2D(JNIEnv *env, jclass clazz, jint target, jint level,
+        jint internalformat, jobject bitmapObj, jint type, jint border)
 {
-    SkBitmap bitmap;
-    bitmap::toSkBitmap(bitmapPtr, &bitmap);
-    SkColorType colorType = bitmap.colorType();
+    graphics::Bitmap bitmap(env, bitmapObj);
+    AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+
     if (internalformat < 0) {
-        internalformat = getInternalFormat(colorType);
+        internalformat = getInternalFormat(bitmapInfo.format);
     }
     if (type < 0) {
-        type = getType(colorType);
+        type = getType(bitmapInfo.format);
     }
-    int err = checkInternalFormat(colorType, internalformat, type);
-    if (err)
-        return err;
-    const int w = bitmap.width();
-    const int h = bitmap.height();
-    const void* p = bitmap.getPixels();
-    if (internalformat == GL_PALETTE8_RGBA8_OES) {
-        err = -1;
-    } else {
-        glTexImage2D(target, level, internalformat, w, h, border,
-                     getPixelFormatFromInternalFormat(internalformat), type, p);
+
+    if (checkInternalFormat(bitmapInfo.format, internalformat, type)) {
+        glTexImage2D(target, level, internalformat, bitmapInfo.width, bitmapInfo.height, border,
+                     getPixelFormatFromInternalFormat(internalformat), type, bitmap.getPixels());
+        return 0;
     }
-    return err;
+    return -1;
 }
 
-static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
-        jint target, jint level, jint xoffset, jint yoffset,
-        jlong bitmapPtr, jint format, jint type)
+static jint util_texSubImage2D(JNIEnv *env, jclass clazz, jint target, jint level,
+        jint xoffset, jint yoffset, jobject bitmapObj, jint format, jint type)
 {
-    SkBitmap bitmap;
-    bitmap::toSkBitmap(bitmapPtr, &bitmap);
-    SkColorType colorType = bitmap.colorType();
-    int internalFormat = getInternalFormat(colorType);
+    graphics::Bitmap bitmap(env, bitmapObj);
+    AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+
+    int internalFormat = getInternalFormat(bitmapInfo.format);
     if (format < 0) {
         format = getPixelFormatFromInternalFormat(internalFormat);
         if (format == GL_PALETTE8_RGBA8_OES)
             return -1; // glCompressedTexSubImage2D() not supported
     }
-    int err = checkInternalFormat(colorType, internalFormat, type);
-    if (err)
-        return err;
-    const int w = bitmap.width();
-    const int h = bitmap.height();
-    const void* p = bitmap.getPixels();
-    glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
-    return 0;
+
+    if (checkInternalFormat(bitmapInfo.format, internalFormat, type)) {
+        glTexSubImage2D(target, level, xoffset, yoffset, bitmapInfo.width, bitmapInfo.height,
+                        format, type, bitmap.getPixels());
+        return 0;
+    }
+    return -1;
 }
 
 /*
@@ -1036,10 +1015,10 @@
 };
 
 static const JNINativeMethod gUtilsMethods[] = {
-    { "native_getInternalFormat", "(J)I", (void*) util_getInternalFormat },
-    { "native_getType", "(J)I", (void*) util_getType },
-    { "native_texImage2D", "(IIIJII)I", (void*)util_texImage2D },
-    { "native_texSubImage2D", "(IIIIJII)I", (void*)util_texSubImage2D },
+    { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
+    { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
+    { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
+    { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
 };
 
 static const JNINativeMethod gEtc1Methods[] = {
diff --git a/core/jni/android_graphics_GraphicBuffer.cpp b/core/jni/android_graphics_GraphicBuffer.cpp
index 43d22eb..b6d5089 100644
--- a/core/jni/android_graphics_GraphicBuffer.cpp
+++ b/core/jni/android_graphics_GraphicBuffer.cpp
@@ -178,9 +178,9 @@
     nativeBuffer.format = AHardwareBuffer_convertFromPixelFormat(buffer->getPixelFormat());
     nativeBuffer.bits = bits;
 
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, &nativeBuffer, ADATASPACE_UNKNOWN);
-    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(&nativeBuffer, ADATASPACE_UNKNOWN);
+    canvas.clipRect({rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -193,8 +193,8 @@
 static jboolean android_graphics_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
         jlong wrapperHandle, jobject canvasObj) {
     // release the buffer from the canvas
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
 
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 4f79790..ed2ce50 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -23,7 +23,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <utils/Log.h>
-#include <android/graphics/GraphicsJNI.h>
+#include <android/graphics/bitmap.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include "core_jni_helpers.h"
@@ -88,7 +88,7 @@
     ScopedLocalRef<jobject> bitmapObj(
             env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
     if (bitmapObj.get()) {
-        GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmap));
+        outPointerIcon->bitmap = graphics::Bitmap(env, bitmapObj.get());
     }
 
     ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
@@ -100,7 +100,7 @@
         outPointerIcon->bitmapFrames.resize(size);
         for (jsize i = 0; i < size; ++i) {
             ScopedLocalRef<jobject> bitmapObj(env, env->GetObjectArrayElement(bitmapFramesObj.get(), i));
-            GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmapFrames[i]));
+            outPointerIcon->bitmapFrames[i] = graphics::Bitmap(env, bitmapObj.get());
         }
     }
 
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index 00bdfb4..908948ea 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -21,8 +21,8 @@
 
 #include <vector>
 
+#include <android/graphics/bitmap.h>
 #include <utils/Errors.h>
-#include <SkBitmap.h>
 
 namespace android {
 
@@ -68,10 +68,10 @@
     }
 
     int32_t style;
-    SkBitmap bitmap;
+    graphics::Bitmap bitmap;
     float hotSpotX;
     float hotSpotY;
-    std::vector<SkBitmap> bitmapFrames;
+    std::vector<graphics::Bitmap> bitmapFrames;
     int32_t durationPerFrame;
 
     inline bool isNullIcon() {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4c2e91f..058a4c8 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -237,12 +237,11 @@
         return 0;
     }
 
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, &buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
 
     if (dirtyRectPtr) {
-        ACanvas_clipRect(canvas, {dirtyRect.left, dirtyRect.top,
-                                  dirtyRect.right, dirtyRect.bottom});
+        canvas.clipRect({dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom});
     }
 
     if (dirtyRectObj) {
@@ -268,8 +267,8 @@
     }
 
     // detach the canvas from the surface
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
 
     // unlock surface
     status_t err = surface->unlockAndPost();
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index bf0f10e..1ca9383 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -458,15 +458,6 @@
     transaction->setInputWindowInfo(ctrl, *handle->getInfo());
 }
 
-static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactionObj,
-        jobject fromTokenObj, jobject toTokenObj) {
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
-    sp<IBinder> fromToken(ibinderForJavaObject(env, fromTokenObj));
-    sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
-    transaction->transferTouchFocus(fromToken, toToken);
-}
-
 static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     transaction->syncInputWindows();
@@ -1381,8 +1372,6 @@
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
             (void*)nativeSetInputWindowInfo },
-    {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)V",
-            (void*)nativeTransferTouchFocus },
     {"nativeSetMetadata", "(JJILandroid/os/Parcel;)V",
             (void*)nativeSetMetadata },
     {"nativeGetDisplayedContentSamplingAttributes",
diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp
index 1ccb6a8..8a3f540 100644
--- a/core/jni/android_view_TextureLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -26,10 +26,7 @@
 #include <gui/GLConsumer.h>
 #include <hwui/Paint.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
 #include <SkMatrix.h>
-#include <SkBlendMode.h>
 
 #include <DeferredLayerUpdater.h>
 #include <Rect.h>
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 1f69c8b..391f515 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -124,9 +124,9 @@
     int32_t status = native_window_lock(window.get(), &outBuffer, &rect);
     if (status) return JNI_FALSE;
 
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, &outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
-    ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(&outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
+    canvas.clipRect({rect.left, rect.top, rect.right, rect.bottom});
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -140,8 +140,8 @@
         jlong nativeWindow, jobject canvasObj) {
 
     // release the buffer from the canvas
-    ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
-    ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+    graphics::Canvas canvas(env, canvasObj);
+    canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
 
     if (nativeWindow) {
         sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index f1df2de0..37d5a411 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -19,7 +19,4 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 1eb6361..58a1868 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -25,7 +25,4 @@
             android:fillEnabled="true"
             android:fillBefore="true" android:fillAfter="true"
             android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_minus_90_frame.xml b/core/res/res/anim/screen_rotate_alpha.xml
similarity index 68%
rename from core/res/res/anim/screen_rotate_minus_90_frame.xml
rename to core/res/res/anim/screen_rotate_alpha.xml
index 2d198f39..c49ef9c 100644
--- a/core/res/res/anim/screen_rotate_minus_90_frame.xml
+++ b/core/res/res/anim/screen_rotate_alpha.xml
@@ -19,10 +19,9 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:shareInterpolator="false">
-    <rotate android:fromDegrees="0" android:toDegrees="90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+           android:interpolator="@interpolator/decelerate_quint"
+           android:fillEnabled="true"
+           android:fillBefore="true" android:fillAfter="true"
+           android:duration="@android:integer/config_mediumAnimTime" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index 9b38939..0927dd3 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -40,9 +40,4 @@
             android:fillEnabled="true"
             android:fillBefore="true" android:fillAfter="true"
             android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index fa34533..fd786f9 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -40,9 +40,4 @@
             android:fillEnabled="true"
             android:fillBefore="true" android:fillAfter="true"
             android:duration="@android:integer/config_mediumAnimTime" />
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_mediumAnimTime" />
 </set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_frame.xml b/core/res/res/anim/screen_rotate_plus_90_frame.xml
deleted file mode 100644
index cd20050..0000000
--- a/core/res/res/anim/screen_rotate_plus_90_frame.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false">
-    <rotate android:fromDegrees="0" android:toDegrees="-90"
-            android:pivotX="50%" android:pivotY="50%"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:fillEnabled="true"
-            android:fillBefore="true" android:fillAfter="true"
-            android:duration="@android:integer/config_longAnimTime" />
-</set>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3d0a3b3..e2f2b2c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1881,15 +1881,14 @@
   <java-symbol type="anim" name="screen_rotate_180_enter" />
   <java-symbol type="anim" name="screen_rotate_180_exit" />
   <java-symbol type="anim" name="screen_rotate_180_frame" />
+  <java-symbol type="anim" name="screen_rotate_alpha"/>
   <java-symbol type="anim" name="screen_rotate_finish_enter" />
   <java-symbol type="anim" name="screen_rotate_finish_exit" />
   <java-symbol type="anim" name="screen_rotate_finish_frame" />
   <java-symbol type="anim" name="screen_rotate_minus_90_enter" />
   <java-symbol type="anim" name="screen_rotate_minus_90_exit" />
-  <java-symbol type="anim" name="screen_rotate_minus_90_frame" />
   <java-symbol type="anim" name="screen_rotate_plus_90_enter" />
   <java-symbol type="anim" name="screen_rotate_plus_90_exit" />
-  <java-symbol type="anim" name="screen_rotate_plus_90_frame" />
   <java-symbol type="anim" name="screen_rotate_start_enter" />
   <java-symbol type="anim" name="screen_rotate_start_exit" />
   <java-symbol type="anim" name="screen_rotate_start_frame" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index a4c504b..c009f58 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -74,7 +74,6 @@
         ":FrameworksCoreTests_install_loc_internal",
         ":FrameworksCoreTests_install_loc_sdcard",
         ":FrameworksCoreTests_install_loc_unspecified",
-        ":FrameworksCoreTests_install_multi_package",
         ":FrameworksCoreTests_install_split_base",
         ":FrameworksCoreTests_install_split_feature_a",
         ":FrameworksCoreTests_install_use_perm_good",
diff --git a/core/tests/coretests/apks/install_multi_package/Android.bp b/core/tests/coretests/apks/install_multi_package/Android.bp
deleted file mode 100644
index 249242e..0000000
--- a/core/tests/coretests/apks/install_multi_package/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-android_test_helper_app {
-    name: "FrameworksCoreTests_install_multi_package",
-    defaults: ["FrameworksCoreTests_apks_defaults"],
-
-    srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml b/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml
deleted file mode 100644
index 5164cae..0000000
--- a/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.frameworks.coretests.install_multi_package">
-
-<!--
-     This manifest is has child packages with components.
--->
-
-    <uses-feature
-        android:name="com.android.frameworks.coretests.nonexistent" />
-    <uses-configuration
-        android:reqFiveWayNav="false" />
-
-    <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="com.android.frameworks.coretests"
-        android:label="Frameworks Core Tests" />
-
-    <permission
-        android:label="test permission"
-        android:name="test_permission"
-        android:protectionLevel="normal" />
-    <uses-permission android:name="android.permission.INTERNET" />
-
-<!--
-     NOTE: This declares a child package, application, then another child
-     package, to test potential bugs that are order-dependent. Also, each
-     one varies the order.
--->
-
-    <package package="com.android.frameworks.coretests.install_multi_package.first_child">
-        <uses-permission android:name="android.permission.NFC" />
-        <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. -->
-        <permission
-            android:label="test permission"
-            android:name="first_child_permission"
-            android:protectionLevel="signature" />
-        <application
-            android:hasCode="true">
-            <activity
-                android:name="com.android.frameworks.coretests.FirstChildTestActivity">
-            </activity>
-            <provider
-                android:name="com.android.frameworks.coretests.FirstChildTestProvider"
-                android:authorities="com.android.frameworks.coretests.testprovider" />
-            <receiver
-                android:name="com.android.frameworks.coretests.FirstChildTestReceiver" />
-            <service
-                android:name="com.android.frameworks.coretests.FirstChildTestService" />
-        </application>
-    </package>
-
-    <application
-        android:hasCode="true">
-        <service
-            android:name="com.android.frameworks.coretests.TestService" />
-        <activity
-            android:name="com.android.frameworks.coretests.TestActivity">
-        </activity>
-        <provider
-            android:name="com.android.frameworks.coretests.TestProvider"
-            android:authorities="com.android.frameworks.coretests.testprovider" />
-        <receiver
-            android:name="com.android.frameworks.coretests.TestReceiver" />
-    </application>
-
-    <package package="com.android.frameworks.coretests.blah.second_child">
-        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-        <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" />
-        <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. -->
-        <permission
-            android:label="test permission"
-            android:name="second_child_permission"
-            android:protectionLevel="dangerous" />
-        <application
-            android:hasCode="true">
-            <receiver
-                android:name="com.android.frameworks.coretests.SecondChildTestReceiver" />
-            <service
-                android:name="com.android.frameworks.coretests.SecondChildTestService" />
-            <activity
-                android:name="com.android.frameworks.coretests.SecondChildTestActivity">
-            </activity>
-            <provider
-                android:name="com.android.frameworks.coretests.SecondChildTestProvider"
-                android:authorities="com.android.frameworks.coretests.testprovider" />
-        </application>
-    </package>
-</manifest>
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java
deleted file mode 100644
index 57afcb0..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Activity;
-
-public class FirstChildTestActivity extends Activity {
-
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java
deleted file mode 100644
index 2816865..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class FirstChildTestProvider extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java
deleted file mode 100644
index ffe84b7..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class FirstChildTestReceiver extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java
deleted file mode 100644
index faa6e9c..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class FirstChildTestService extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java
deleted file mode 100644
index e89f264..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Activity;
-
-public class SecondChildTestActivity extends Activity {
-
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java
deleted file mode 100644
index 2bd40a5..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class SecondChildTestProvider extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java
deleted file mode 100644
index a6c4ddc..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class SecondChildTestReceiver extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java
deleted file mode 100644
index 59f9f10..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class TestProvider extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java
deleted file mode 100644
index 21f6263..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-
-public class TestReceiver extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        return false;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return null;
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java
deleted file mode 100644
index b330e75..0000000
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.frameworks.coretests;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class TestService extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
deleted file mode 100644
index cc48239..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link AndroidHidlUpdater}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
-    private static final String OTHER_LIBRARY = "other.library";
-
-    @Test
-    public void targeted_at_P() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.P);
-
-        // no change, not system
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_P_system() {
-        PackageBuilder before = builder().asSystemApp()
-                .targetSdkVersion(Build.VERSION_CODES.P);
-
-        // Should add both HIDL libraries
-        PackageBuilder after = builder().asSystemApp()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_P_not_empty_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // no change, not system
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_P_not_empty_usesLibraries_system() {
-        PackageBuilder before = builder().asSystemApp()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // The hidl jars should be added at the start of the list because it
-        // is not on the bootclasspath and the package targets pre-P.
-        PackageBuilder after = builder().asSystemApp()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE, OTHER_LIBRARY);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_P_in_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.P);
-
-        // Libraries are removed because they are not available for non-system apps
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_P_in_usesLibraries_system() {
-        PackageBuilder before = builder().asSystemApp()
-                .targetSdkVersion(Build.VERSION_CODES.P)
-                .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE);
-
-        // No change is required because the package explicitly requests the HIDL libraries
-        // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ANDROID_HIDL_BASE);
-
-        // Dependency is removed, it is not available.
-        PackageBuilder after = builder();
-
-        // Libraries are removed because they are not available for apps targetting Q+
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ANDROID_HIDL_BASE);
-
-        // Dependency is removed, it is not available.
-        PackageBuilder after = builder();
-
-        // Libraries are removed because they are not available for apps targetting Q+
-        checkBackwardsCompatibility(before, after);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
deleted file mode 100644
index 03108ce..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link AndroidTestBaseUpdater}
- */
-@SmallTest
-@RunWith(OptionalClassRunner.class)
[email protected]("android.content.pm.AndroidTestBaseUpdater")
-public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
-    private static final String OTHER_LIBRARY = "other.library";
-
-    @Test
-    public void targeted_at_Q() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q);
-
-        // Should add org.apache.http.legacy.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q)
-                .requiredLibraries(ANDROID_TEST_BASE);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_Q_not_empty_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // The org.apache.http.legacy jar should be added at the start of the list because it
-        // is not on the bootclasspath and the package targets pre-Q.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q)
-                .requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_Q_in_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q)
-                .requiredLibraries(ANDROID_TEST_BASE);
-
-        // No change is required because although org.apache.http.legacy has been removed from
-        // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_Q_in_usesOptionalLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.Q)
-                .optionalLibraries(ANDROID_TEST_BASE);
-
-        // No change is required because although org.apache.http.legacy has been removed from
-        // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
-
-        // No change is required because the package explicitly requests org.apache.http.legacy
-        // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
-
-        // No change is required because the package explicitly requests org.apache.http.legacy
-        // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
deleted file mode 100644
index 7f817d6..0000000
--- a/core/tests/coretests/src/android/content/pm/AndroidTestRunnerSplitUpdaterTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
-
-import android.content.pm.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link AndroidTestRunnerSplitUpdater}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class AndroidTestRunnerSplitUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
-    @Test
-    public void android_test_runner_in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_RUNNER);
-
-        PackageBuilder after = builder()
-                .optionalLibraries(ANDROID_TEST_MOCK, ANDROID_TEST_RUNNER);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
-        PackageBuilder before = builder()
-                .requiredLibraries(ANDROID_TEST_RUNNER)
-                .optionalLibraries(ANDROID_TEST_MOCK);
-
-        PackageBuilder after = builder()
-                .requiredLibraries(ANDROID_TEST_RUNNER)
-                .optionalLibraries(ANDROID_TEST_MOCK);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        checkBackwardsCompatibility(before, after, AndroidTestRunnerSplitUpdater::new);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
deleted file mode 100644
index 834a0bb..0000000
--- a/core/tests/coretests/src/android/content/pm/OrgApacheHttpLegacyUpdaterTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link OrgApacheHttpLegacyUpdater}
- */
-@SmallTest
-@RunWith(OptionalClassRunner.class)
[email protected]("android.content.pm.OrgApacheHttpLegacyUpdater")
-public class OrgApacheHttpLegacyUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
-    private static final String OTHER_LIBRARY = "other.library";
-
-    @Test
-    public void targeted_at_O() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        // Should add org.apache.http.legacy.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_O_not_empty_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // The org.apache.http.legacy jar should be added at the start of the list because it
-        // is not on the bootclasspath and the package targets pre-P.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(ORG_APACHE_HTTP_LEGACY, OTHER_LIBRARY);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // No change is required because although org.apache.http.legacy has been removed from
-        // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesOptionalLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // No change is required because although org.apache.http.legacy has been removed from
-        // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // No change is required because the package explicitly requests org.apache.http.legacy
-        // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // No change is required because the package explicitly requests org.apache.http.legacy
-        // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        checkBackwardsCompatibility(before, after, OrgApacheHttpLegacyUpdater::new);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
deleted file mode 100644
index ad9814b..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-
-import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Assume;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@SmallTest
-@RunWith(JUnit4.class)
-public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdaterTest {
-
-    @Test
-    public void null_usesLibraries_and_usesOptionalLibraries() {
-        PackageBuilder before = builder();
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    /**
-     * Detect when the android.test.base is not on the bootclasspath.
-     *
-     * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
-     * succeed otherwise. This allows a developer to ensure that the tests are being run in the
-     * correct environment.
-     */
-    @Test
-    public void detectWhenATBisOnBCP() {
-        Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsATB());
-    }
-
-    /**
-     * Ensures that the {@link PackageBackwardCompatibility} uses {@link OrgApacheHttpLegacyUpdater}
-     * and {@link AndroidTestBaseUpdater} when necessary.
-     *
-     * <p>More comprehensive tests for that class can be found in
-     * {@link OrgApacheHttpLegacyUpdaterTest}.
-     */
-    @Test
-    public void targeted_at_O() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        List<String> expected = new ArrayList<>();
-        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
-            expected.add(ANDROID_TEST_BASE);
-        }
-        expected.add(ORG_APACHE_HTTP_LEGACY);
-
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(expected);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    /**
-     * Ensures that the {@link PackageBackwardCompatibility} uses
-     * {@link RemoveUnnecessaryAndroidTestBaseLibrary}
-     * when necessary.
-     *
-     * <p>More comprehensive tests for that class can be found in
-     * {@link RemoveUnnecessaryAndroidTestBaseLibraryTest}.
-     */
-    @Test
-    public void android_test_base_in_usesLibraries() {
-        Assume.assumeTrue("Test requires that "
-                        + ANDROID_TEST_BASE + " is on the bootclasspath",
-                PackageBackwardCompatibility.bootClassPathContainsATB());
-
-        PackageBuilder before = builder()
-                .requiredLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    /**
-     * Ensures that the {@link PackageBackwardCompatibility} uses a
-     * {@link PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater}.
-     *
-     * <p>More comprehensive tests for that class can be found in
-     * {@link AndroidTestRunnerSplitUpdaterTest}.
-     */
-    @Test
-    public void android_test_runner_in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_RUNNER);
-
-        List<String> expected = new ArrayList<>();
-        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
-            expected.add(ANDROID_TEST_BASE);
-        }
-        expected.add(ANDROID_TEST_MOCK);
-        expected.add(ANDROID_TEST_RUNNER);
-
-        PackageBuilder after = builder()
-                .requiredLibraries(expected);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
deleted file mode 100644
index f7544af..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Build;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Test support for building {@link PackageParser.Package} instances.
- */
-class PackageBuilder {
-
-    private int mTargetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-
-    private int mFlags = 0;
-
-    private ArrayList<String> mRequiredLibraries;
-
-    private ArrayList<String> mOptionalLibraries;
-
-    public static PackageBuilder builder() {
-        return new PackageBuilder();
-    }
-
-    public PackageParser.Package build() {
-        PackageParser.Package pkg = new PackageParser.Package("org.package.name");
-        pkg.applicationInfo.targetSdkVersion = mTargetSdkVersion;
-        pkg.applicationInfo.flags = mFlags;
-        pkg.usesLibraries = mRequiredLibraries;
-        pkg.usesOptionalLibraries = mOptionalLibraries;
-        return pkg;
-    }
-
-    PackageBuilder targetSdkVersion(int version) {
-        this.mTargetSdkVersion = version;
-        return this;
-    }
-
-    PackageBuilder asSystemApp() {
-        this.mFlags |= ApplicationInfo.FLAG_SYSTEM;
-        return this;
-    }
-
-    PackageBuilder requiredLibraries(String... names) {
-        this.mRequiredLibraries = arrayListOrNull(names);
-        return this;
-    }
-
-    PackageBuilder requiredLibraries(List<String> names) {
-        this.mRequiredLibraries = arrayListOrNull(names.toArray(new String[names.size()]));
-        return this;
-    }
-
-    PackageBuilder optionalLibraries(String... names) {
-        this.mOptionalLibraries = arrayListOrNull(names);
-        return this;
-    }
-
-    /**
-     * Check that this matches the supplied {@link PackageParser.Package}.
-     *
-     * @param pkg the instance to compare with this.
-     */
-    public void check(PackageParser.Package pkg) {
-        assertEquals("targetSdkVersion should not be changed",
-                mTargetSdkVersion,
-                pkg.applicationInfo.targetSdkVersion);
-        assertEquals("usesLibraries not updated correctly",
-                mRequiredLibraries,
-                pkg.usesLibraries);
-        assertEquals("usesOptionalLibraries not updated correctly",
-                mOptionalLibraries,
-                pkg.usesOptionalLibraries);
-    }
-
-    private static ArrayList<String> arrayListOrNull(String... strings) {
-        if (strings == null || strings.length == 0) {
-            return null;
-        }
-        ArrayList<String> list = new ArrayList<>();
-        Collections.addAll(list, strings);
-        return list;
-    }
-
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 5c7f2af..cb23850 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -23,26 +23,26 @@
 
 import android.apex.ApexInfo;
 import android.content.Context;
-import android.content.pm.PackageParser.Component;
-import android.content.pm.PackageParser.Package;
-import android.content.pm.PackageParser.Permission;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
-import android.os.SystemProperties;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
+import com.android.internal.util.ArrayUtils;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.InputStream;
-import java.util.Arrays;
 import java.util.function.Function;
 
 @SmallTest
@@ -59,8 +59,8 @@
     private static final String PRE_RELEASE_WITH_FINGERPRINT = "B.fingerprint";
     private static final String NEWER_PRE_RELEASE_WITH_FINGERPRINT = "C.fingerprint";
 
-    private static final String[] CODENAMES_RELEASED = { /* empty */ };
-    private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE };
+    private static final String[] CODENAMES_RELEASED = { /* empty */};
+    private static final String[] CODENAMES_PRE_RELEASE = {PRE_RELEASE};
 
     private static final int OLDER_VERSION = 10;
     private static final int PLATFORM_VERSION = 20;
@@ -300,10 +300,6 @@
         assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
     }
 
-    Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
-        return parsePackage(apkFileName, apkResourceId, p -> p);
-    }
-
     /**
      * Copies a specified {@code resourceId} to a file. Returns a non-null file if the copy
      * succeeded, or {@code null} otherwise.
@@ -331,16 +327,17 @@
      *
      * APKs are put into coretests/apks/packageparser_*.
      *
-     * @param apkFileName temporary file name to store apk extracted from resources
+     * @param apkFileName   temporary file name to store apk extracted from resources
      * @param apkResourceId identifier of the apk as a resource
      */
-    Package parsePackage(String apkFileName, int apkResourceId,
-            Function<Package, Package> converter) throws Exception {
+    ParsedPackage parsePackage(String apkFileName, int apkResourceId,
+            Function<ParsedPackage, ParsedPackage> converter) throws Exception {
         // Copy the resource to a file.
         File outFile = null;
         try {
             outFile = copyRawResourceToFile(apkFileName, apkResourceId);
-            return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
+            return converter.apply(
+                    new PackageParser().parseParsedPackage(outFile, 0 /* flags */, false));
         } finally {
             if (outFile != null) {
                 outFile.delete();
@@ -351,40 +348,40 @@
     /**
      * Asserts basic properties about a component.
      */
-    private void assertComponent(String className, String packageName, int numIntents,
-            Component<?> component) {
+    private void assertComponent(String className, int numIntents, ParsedComponent<?> component) {
         assertEquals(className, component.className);
-        assertEquals(packageName, component.owner.packageName);
         assertEquals(numIntents, component.intents.size());
     }
 
     /**
      * Asserts four regularly-named components of each type: one Activity, one Service, one
      * Provider, and one Receiver.
+     *
      * @param template templated string with %s subbed with Activity, Service, Provider, Receiver
      */
-    private void assertOneComponentOfEachType(String template, Package p) {
-        String packageName = p.packageName;
+    private void assertOneComponentOfEachType(String template, AndroidPackage p) {
+        assertEquals(2, p.getActivities().size());
 
-        assertEquals(1, p.activities.size());
+        // For normal apps, a Activity that forwards to the App Details page is added.
+        assertEquals("android.app.AppDetailsActivity", p.getActivities().get(1)
+                .className);
+
         assertComponent(String.format(template, "Activity"),
-                packageName, 0 /* intents */, p.activities.get(0));
-        assertEquals(1, p.services.size());
+                0 /* intents */, p.getActivities().get(0));
+        assertEquals(1, p.getServices().size());
         assertComponent(String.format(template, "Service"),
-                packageName, 0 /* intents */, p.services.get(0));
-        assertEquals(1, p.providers.size());
+                0 /* intents */, p.getServices().get(0));
+        assertEquals(1, p.getProviders().size());
         assertComponent(String.format(template, "Provider"),
-                packageName, 0 /* intents */, p.providers.get(0));
-        assertEquals(1, p.receivers.size());
+                0 /* intents */, p.getProviders().get(0));
+        assertEquals(1, p.getReceivers().size());
         assertComponent(String.format(template, "Receiver"),
-                packageName, 0 /* intents */, p.receivers.get(0));
+                0 /* intents */, p.getReceivers().get(0));
     }
 
-    private void assertPermission(String name, String packageName, int protectionLevel,
-            Permission permission) {
-        assertEquals(packageName, permission.owner.packageName);
-        assertEquals(name, permission.info.name);
-        assertEquals(protectionLevel, permission.info.protectionLevel);
+    private void assertPermission(String name, int protectionLevel, ParsedPermission permission) {
+        assertEquals(name, permission.getName());
+        assertEquals(protectionLevel, permission.getProtection());
     }
 
     private void assertMetadata(Bundle b, String... keysAndValues) {
@@ -416,25 +413,25 @@
     }
 
     private void checkPackageWithComponents(
-            Function<Package, Package> converter) throws Exception {
-        Package p = parsePackage(
+            Function<ParsedPackage, ParsedPackage> converter) throws Exception {
+        ParsedPackage p = parsePackage(
                 "install_complete_package_info.apk", R.raw.install_complete_package_info,
                 converter);
         String packageName = "com.android.frameworks.coretests.install_complete_package_info";
 
-        assertEquals(packageName, p.packageName);
-        assertEquals(1, p.permissions.size());
+        assertEquals(packageName, p.getPackageName());
+        assertEquals(1, p.getPermissions().size());
         assertPermission(
                 "com.android.frameworks.coretests.install_complete_package_info.test_permission",
-                packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0));
+                PermissionInfo.PROTECTION_NORMAL, p.getPermissions().get(0));
 
         // Hidden "app details" activity is added to every package.
         boolean foundAppDetailsActivity = false;
-        for (int i = 0; i < p.activities.size(); i++) {
-            if (p.activities.get(i).className.equals(
+        for (int i = 0; i < ArrayUtils.size(p.getActivities()); i++) {
+            if (p.getActivities().get(i).className.equals(
                     PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME)) {
                 foundAppDetailsActivity = true;
-                p.activities.remove(i);
+                p.getActivities().remove(i);
                 break;
             }
         }
@@ -442,72 +439,23 @@
 
         assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
 
-        assertMetadata(p.mAppMetaData,
+        assertMetadata(p.getAppMetaData(),
                 "key1", "value1",
                 "key2", "this_is_app");
-        assertMetadata(p.activities.get(0).metaData,
+        assertMetadata(p.getActivities().get(0).getMetaData(),
                 "key1", "value1",
                 "key2", "this_is_activity");
-        assertMetadata(p.services.get(0).metaData,
+        assertMetadata(p.getServices().get(0).getMetaData(),
                 "key1", "value1",
                 "key2", "this_is_service");
-        assertMetadata(p.receivers.get(0).metaData,
+        assertMetadata(p.getReceivers().get(0).getMetaData(),
                 "key1", "value1",
                 "key2", "this_is_receiver");
-        assertMetadata(p.providers.get(0).metaData,
+        assertMetadata(p.getProviders().get(0).getMetaData(),
                 "key1", "value1",
                 "key2", "this_is_provider");
     }
 
-    /**
-     * Determines if the current device supports multi-package APKs.
-     */
-    private boolean supportsMultiPackageApk() {
-        return SystemProperties.getBoolean("persist.sys.child_packages_enabled", false);
-    }
-
-    @Test
-    public void testMultiPackageComponents() throws Exception {
-        // TODO(gboyer): Remove once we decide to launch multi-package APKs.
-        if (!supportsMultiPackageApk()) {
-            return;
-        }
-        String parentName = "com.android.frameworks.coretests.install_multi_package";
-        String firstChildName =
-                "com.android.frameworks.coretests.install_multi_package.first_child";
-        String secondChildName =  // NOTE: intentionally inconsistent!
-                "com.android.frameworks.coretests.blah.second_child";
-
-        Package parent = parsePackage("install_multi_package.apk", R.raw.install_multi_package);
-        assertEquals(parentName, parent.packageName);
-        assertEquals(2, parent.childPackages.size());
-        assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", parent);
-        assertEquals(1, parent.permissions.size());
-        assertPermission(parentName + ".test_permission", parentName,
-                PermissionInfo.PROTECTION_NORMAL, parent.permissions.get(0));
-        assertEquals(Arrays.asList("android.permission.INTERNET"),
-                parent.requestedPermissions);
-
-        Package firstChild = parent.childPackages.get(0);
-        assertEquals(firstChildName, firstChild.packageName);
-        assertOneComponentOfEachType(
-                "com.android.frameworks.coretests.FirstChildTest%s", firstChild);
-        assertEquals(0, firstChild.permissions.size());  // Child APKs cannot declare permissions.
-        assertEquals(Arrays.asList("android.permission.NFC"),
-                firstChild.requestedPermissions);
-
-        Package secondChild = parent.childPackages.get(1);
-        assertEquals(secondChildName, secondChild.packageName);
-        assertOneComponentOfEachType(
-                "com.android.frameworks.coretests.SecondChildTest%s", secondChild);
-        assertEquals(0, secondChild.permissions.size());  // Child APKs cannot declare permissions.
-        assertEquals(
-                Arrays.asList(
-                        "android.permission.ACCESS_NETWORK_STATE",
-                        "android.permission.READ_CONTACTS"),
-                secondChild.requestedPermissions);
-    }
-
     @Test
     public void testApexPackageInfoGeneration() throws Exception {
         String apexModuleName = "com.android.tzdata.apex";
@@ -522,7 +470,7 @@
         int flags = PackageManager.GET_META_DATA | PackageManager.GET_SIGNING_CERTIFICATES;
 
         PackageParser pp = new PackageParser();
-        Package p = pp.parsePackage(apexFile, flags, false);
+        PackageParser.Package p = pp.parsePackage(apexFile, flags, false);
         PackageParser.collectCertificates(p, false);
         PackageInfo pi = PackageParser.generatePackageInfo(p, apexInfo, flags);
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
deleted file mode 100644
index 71a0e5e..0000000
--- a/core/tests/coretests/src/android/content/pm/PackageSharedLibraryUpdaterTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import java.util.function.Supplier;
-
-/**
- * Helper for classes that test {@link PackageSharedLibraryUpdater}.
- */
-abstract class PackageSharedLibraryUpdaterTest {
-
-    static void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after,
-            Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
-        PackageParser.Package pkg = before.build();
-        updaterSupplier.get().updatePackage(pkg);
-        after.check(pkg);
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
deleted file mode 100644
index 216b0c8..0000000
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
-
-import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class RemoveUnnecessaryAndroidTestBaseLibraryTest
-        extends PackageSharedLibraryUpdaterTest {
-
-    private static final String OTHER_LIBRARY = "other.library";
-
-    @Test
-    public void targeted_at_O() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        // No change required.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_O_not_empty_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // No change required.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesOptionalLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .optionalLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_bothLibraries() {
-        PackageBuilder before = builder()
-                .requiredLibraries(ANDROID_TEST_BASE)
-                .optionalLibraries(ANDROID_TEST_BASE);
-
-        // android.test.base should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
-        // PackageBackwardCompatibility and that seems to create a package-private lambda in
-        // android.content.pm which this then tries to reuse but fails because it cannot access
-        // package-private classes/members because the test is loaded by a different ClassLoader
-        // than the lambda.
-        checkBackwardsCompatibility(before, after,
-                () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
-    }
-
-}
diff --git a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
deleted file mode 100644
index fc60980..0000000
--- a/core/tests/coretests/src/android/content/pm/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-
-import android.content.pm.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link RemoveUnnecessaryOrgApacheHttpLegacyLibrary}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest
-        extends PackageSharedLibraryUpdaterTest {
-
-    private static final String OTHER_LIBRARY = "other.library";
-
-    @Test
-    public void targeted_at_O() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        // No change required.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_O_not_empty_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(OTHER_LIBRARY);
-
-        // No change required.
-        checkBackwardsCompatibility(before, before);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // org.apache.http.legacy should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void targeted_at_O_in_usesOptionalLibraries() {
-        PackageBuilder before = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O)
-                .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // org.apache.http.legacy should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder()
-                .targetSdkVersion(Build.VERSION_CODES.O);
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_usesLibraries() {
-        PackageBuilder before = builder().requiredLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // org.apache.http.legacy should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_usesOptionalLibraries() {
-        PackageBuilder before = builder().optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // org.apache.http.legacy should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    @Test
-    public void in_bothLibraries() {
-        PackageBuilder before = builder()
-                .requiredLibraries(ORG_APACHE_HTTP_LEGACY)
-                .optionalLibraries(ORG_APACHE_HTTP_LEGACY);
-
-        // org.apache.http.legacy should be removed from the libraries because it is provided
-        // on the bootclasspath and providing both increases start up cost unnecessarily.
-        PackageBuilder after = builder();
-
-        checkBackwardsCompatibility(before, after);
-    }
-
-    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
-        // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
-        // PackageBackwardCompatibility and that seems to create a package-private lambda in
-        // android.content.pm which this then tries to reuse but fails because it cannot access
-        // package-private classes/members because the test is loaded by a different ClassLoader
-        // than the lambda.
-        checkBackwardsCompatibility(before, after,
-                () -> new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
-    }
-}
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index 49849ee..1e0bfb0 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -25,9 +25,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.FileUtils;
 
 import androidx.test.InstrumentationRegistry;
@@ -36,7 +36,6 @@
 
 import com.android.frameworks.coretests.R;
 
-import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -95,13 +94,13 @@
     public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         createDexMetadataFile("install_split_base.apk");
-        Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+        ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
 
         Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
         assertEquals(1, packageDexMetadata.size());
-        String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
         assertNotNull(baseDexMetadata);
-        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
     }
 
     @Test
@@ -111,17 +110,17 @@
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_base.apk");
         createDexMetadataFile("install_split_feature_a.apk");
-        Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+        ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
 
         Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
         assertEquals(2, packageDexMetadata.size());
-        String baseDexMetadata = packageDexMetadata.get(pkg.baseCodePath);
+        String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
         assertNotNull(baseDexMetadata);
-        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.baseCodePath));
+        assertTrue(isDexMetadataForApk(baseDexMetadata, pkg.getBaseCodePath()));
 
-        String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+        String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
         assertNotNull(splitDexMetadata);
-        assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+        assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
     }
 
     @Test
@@ -130,14 +129,14 @@
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
         copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
         createDexMetadataFile("install_split_feature_a.apk");
-        Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+        ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
 
         Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
         assertEquals(1, packageDexMetadata.size());
 
-        String splitDexMetadata = packageDexMetadata.get(pkg.splitCodePaths[0]);
+        String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
         assertNotNull(splitDexMetadata);
-        assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.splitCodePaths[0]));
+        assertTrue(isDexMetadataForApk(splitDexMetadata, pkg.getSplitCodePaths()[0]));
     }
 
     @Test
@@ -146,7 +145,8 @@
         File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
         Files.createFile(invalidDmFile.toPath());
         try {
-            PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+            ParsedPackage pkg = new PackageParser()
+                    .parseParsedPackage(mTmpDir, 0 /* flags */, false);
             DexMetadataHelper.validatePackageDexMetadata(pkg);
         } catch (PackageParserException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
@@ -163,7 +163,8 @@
         Files.createFile(invalidDmFile.toPath());
 
         try {
-            PackageParser.Package pkg = new PackageParser().parsePackage(mTmpDir, 0 /* flags */);
+            ParsedPackage pkg = new PackageParser()
+                    .parseParsedPackage(mTmpDir, 0 /* flags */, false);
             DexMetadataHelper.validatePackageDexMetadata(pkg);
         } catch (PackageParserException e) {
             assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
new file mode 100644
index 0000000..21479c0
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidHidlUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_P() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // no change, not system
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_P_system() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .hideAsParsed()
+                .setSystem(true);
+
+        // Should add both HIDL libraries
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(ANDROID_HIDL_MANAGER)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed()
+                .setSystem(true)
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_P_not_empty_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // no change, not system
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_P_not_empty_usesLibraries_system() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .setSystem(true);
+
+        // The hidl jars should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-P.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(ANDROID_HIDL_MANAGER)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .setSystem(true)
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_P_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(ANDROID_HIDL_MANAGER)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // Libraries are removed because they are not available for non-system apps
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_P_in_usesLibraries_system() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(ANDROID_HIDL_MANAGER)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed()
+                .setSystem(true);
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.P)
+                .addUsesLibrary(ANDROID_HIDL_MANAGER)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed()
+                .setSystem(true)
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests the HIDL libraries
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed();
+
+        // Dependency is removed, it is not available.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // Libraries are removed because they are not available for apps targeting Q+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_HIDL_BASE)
+                .hideAsParsed();
+
+        // Dependency is removed, it is not available.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // Libraries are removed because they are not available for apps targeting Q+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
new file mode 100644
index 0000000..65ae219
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.content.pm.OptionalClassRunner;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link AndroidTestBaseUpdater}
+ */
+@SmallTest
+@RunWith(OptionalClassRunner.class)
[email protected]("android.content.pm.parsing.library.AndroidTestBaseUpdater")
+public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_Q() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .hideAsParsed();
+
+        // Should add org.apache.http.legacy.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_not_empty_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed();
+
+        // The org.apache.http.legacy jar should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-Q.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.Q)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
new file mode 100644
index 0000000..38755b9
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidTestRunnerSplitUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidTestRunnerSplitUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    @Test
+    public void android_test_runner_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+                .addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_RUNNER)
+                .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_RUNNER)
+                .addUsesOptionalLibrary(ANDROID_TEST_MOCK)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, AndroidTestRunnerSplitUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
new file mode 100644
index 0000000..4c7899b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.OptionalClassRunner;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link OrgApacheHttpLegacyUpdater}
+ */
+@SmallTest
+@RunWith(OptionalClassRunner.class)
[email protected]("android.content.pm.parsing.library.OrgApacheHttpLegacyUpdater")
+public class OrgApacheHttpLegacyUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed();
+
+        // Should add org.apache.http.legacy.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed();
+
+        // The org.apache.http.legacy jar should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-P.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because although org.apache.http.legacy has been removed from
+        // the bootclasspath the package explicitly requests it.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, OrgApacheHttpLegacyUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
new file mode 100644
index 0000000..00d468d
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdaterTest {
+
+    @Test
+    public void null_usesLibraries_and_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    /**
+     * Detect when the android.test.base is not on the bootclasspath.
+     *
+     * <p>This test will be ignored when org.apache.http.legacy is not on the bootclasspath and
+     * succeed otherwise. This allows a developer to ensure that the tests are being run in the
+     * correct environment.
+     */
+    @Test
+    public void detectWhenATBisOnBCP() {
+        Assume.assumeTrue(PackageBackwardCompatibility.bootClassPathContainsATB());
+    }
+
+    /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses {@link OrgApacheHttpLegacyUpdater}
+     * and {@link AndroidTestBaseUpdater} when necessary.
+     *
+     * <p>More comprehensive tests for that class can be found in
+     * {@link OrgApacheHttpLegacyUpdaterTest}.
+     */
+    @Test
+    public void targeted_at_O() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed();
+
+        ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .setTargetSdkVersion(Build.VERSION_CODES.O);
+
+        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+            after.addUsesLibrary(ANDROID_TEST_BASE);
+        }
+        after.addUsesLibrary(ORG_APACHE_HTTP_LEGACY);
+
+        checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+    }
+
+    /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses
+     * {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+     * when necessary.
+     *
+     * <p>More comprehensive tests for that class can be found in
+     * {@link RemoveUnnecessaryAndroidTestBaseLibraryTest}.
+     */
+    @Test
+    public void android_test_base_in_usesLibraries() {
+        Assume.assumeTrue("Test requires that "
+                        + ANDROID_TEST_BASE + " is on the bootclasspath",
+                PackageBackwardCompatibility.bootClassPathContainsATB());
+
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses a
+     * {@link PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater}.
+     *
+     * <p>More comprehensive tests for that class can be found in
+     * {@link AndroidTestRunnerSplitUpdaterTest}.
+     */
+    @Test
+    public void android_test_runner_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_RUNNER)
+                .hideAsParsed();
+
+        ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
+            after.addUsesLibrary(ANDROID_TEST_BASE);
+        }
+        after.addUsesLibrary(ANDROID_TEST_MOCK);
+        after.addUsesLibrary(ANDROID_TEST_RUNNER);
+
+        checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
new file mode 100644
index 0000000..e7a80e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
+
+import java.util.function.Supplier;
+
+/**
+ * Helper for classes that test {@link PackageSharedLibraryUpdater}.
+ */
+abstract class PackageSharedLibraryUpdaterTest {
+
+    protected static final String PACKAGE_NAME = "org.package.name";
+
+    static void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
+        updaterSupplier.get().updatePackage(before);
+        check(before.hideAsFinal(), after);
+    }
+
+    private static void check(AndroidPackage before, AndroidPackage after) {
+        assertEquals("targetSdkVersion should not be changed",
+                after.getTargetSdkVersion(),
+                before.getTargetSdkVersion());
+        assertEquals("usesLibraries not updated correctly",
+                after.getUsesLibraries(),
+                before.getUsesLibraries());
+        assertEquals("usesOptionalLibraries not updated correctly",
+                after.getUsesOptionalLibraries(),
+                before.getUsesOptionalLibraries());
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
new file mode 100644
index 0000000..fd3ba2b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class RemoveUnnecessaryAndroidTestBaseLibraryTest
+        extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change required.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change required.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_bothLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ANDROID_TEST_BASE)
+                .addUsesOptionalLibrary(ANDROID_TEST_BASE)
+                .hideAsParsed();
+
+        // android.test.base should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
+        // PackageBackwardCompatibility and that seems to create a package-private lambda in
+        // android.content.pm which this then tries to reuse but fails because it cannot access
+        // package-private classes/members because the test is loaded by a different ClassLoader
+        // than the lambda.
+        checkBackwardsCompatibility(before, after,
+                () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
+    }
+
+}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
new file mode 100644
index 0000000..d3494d9
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.library;
+
+import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link RemoveUnnecessaryOrgApacheHttpLegacyLibrary}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest
+        extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+
+    @Test
+    public void targeted_at_O() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change required.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_not_empty_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed();
+
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(OTHER_LIBRARY)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        // No change required.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        // org.apache.http.legacy should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_O_in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        // org.apache.http.legacy should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        // org.apache.http.legacy should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        // org.apache.http.legacy should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_bothLibraries() {
+        ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
+                .addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
+                .hideAsParsed();
+
+        // org.apache.http.legacy should be removed from the libraries because it is provided
+        // on the bootclasspath and providing both increases start up cost unnecessarily.
+        AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed()
+                .hideAsFinal();
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
+        // PackageBackwardCompatibility and that seems to create a package-private lambda in
+        // android.content.pm which this then tries to reuse but fails because it cannot access
+        // package-private classes/members because the test is loaded by a different ClassLoader
+        // than the lambda.
+        checkBackwardsCompatibility(before, after,
+                () -> new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
+    }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f32935f..2dfb777 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1,11 +1,11 @@
 {
   "version": "1.0.0",
   "messages": {
-    "594230385": {
+    "676824470": {
       "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
       "level": "ERROR",
       "group": "TEST_GROUP",
-      "at": "com\/android\/server\/wm\/ProtoLogGroup.java:94"
+      "at": "com\/android\/server\/wm\/ProtoLogGroup.java"
     }
   },
   "groups": {
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 072beae..c692097 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -323,14 +323,16 @@
         <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
     </family>
     <family lang="und-Mymr" variant="elegant">
-        <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
-        <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
+        <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
+        <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
+        <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
         <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
         <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
     </family>
     <family lang="und-Mymr" variant="compact">
-        <font weight="400" style="normal">NotoSansMyanmarUI-Regular-ZawDecode.ttf</font>
-        <font weight="700" style="normal">NotoSansMyanmarUI-Bold-ZawDecode.ttf</font>
+        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
+        <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
+        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
     </family>
     <family lang="und-Thaa">
         <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index b017384..f839213e 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -43,41 +43,28 @@
     if (!matrix.rectStaysRect()) return true;
     SkRect dstDevRect = matrix.mapRect(dstRect);
     float dstW, dstH;
-    bool requiresIntegerTranslate = false;
     if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) {
         // Has a 90 or 270 degree rotation, although total matrix may also have scale factors
         // in m10 and m01. Those scalings are automatically handled by mapRect so comparing
         // dimensions is sufficient, but swap width and height comparison.
         dstW = dstDevRect.height();
         dstH = dstDevRect.width();
-        requiresIntegerTranslate = true;
     } else {
         // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but
         // dimensions are still safe to compare directly.
         dstW = dstDevRect.width();
         dstH = dstDevRect.height();
-        requiresIntegerTranslate =
-                matrix.getScaleX() < -NON_ZERO_EPSILON || matrix.getScaleY() < -NON_ZERO_EPSILON;
     }
     if (!(MathUtils::areEqual(dstW, srcRect.width()) &&
           MathUtils::areEqual(dstH, srcRect.height()))) {
         return true;
     }
-    if (requiresIntegerTranslate) {
-        // Device rect and source rect should be integer aligned to ensure there's no difference
-        // in how nearest-neighbor sampling is resolved.
-        return !(isIntegerAligned(srcRect.x()) &&
-                 isIntegerAligned(srcRect.y()) &&
-                 isIntegerAligned(dstDevRect.x()) &&
-                 isIntegerAligned(dstDevRect.y()));
-    } else {
-        // As long as src and device rects are translated by the same fractional amount,
-        // filtering won't be needed
-        return !(MathUtils::areEqual(SkScalarFraction(srcRect.x()),
-                                     SkScalarFraction(dstDevRect.x())) &&
-                 MathUtils::areEqual(SkScalarFraction(srcRect.y()),
-                                     SkScalarFraction(dstDevRect.y())));
-    }
+    // Device rect and source rect should be integer aligned to ensure there's no difference
+    // in how nearest-neighbor sampling is resolved.
+    return !(isIntegerAligned(srcRect.x()) &&
+             isIntegerAligned(srcRect.y()) &&
+             isIntegerAligned(dstDevRect.x()) &&
+             isIntegerAligned(dstDevRect.y()));
 }
 
 bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 16f2917..6bb896fd 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,11 +20,11 @@
     ],
 
     shared_libs: [
+        "libandroid_runtime",
         "libbinder",
         "libcutils",
         "liblog",
         "libutils",
-        "libhwui",
         "libgui",
         "libui",
         "libinput",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abf0837..e4348f2a 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,12 +24,6 @@
 
 #include <log/log.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-#include <SkBlendMode.h>
-
 namespace android {
 
 // --- WeakLooperCallback ---
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index fd386e9..804644c 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -23,11 +23,9 @@
 #include <utils/String8.h>
 #include <gui/Surface.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
+#include <android/graphics/bitmap.h>
+#include <android/graphics/canvas.h>
+#include <android/graphics/paint.h>
 #include <android/native_window.h>
 
 namespace android {
@@ -132,8 +130,8 @@
         SpriteUpdate& update = updates.editItemAt(i);
 
         if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
-            update.state.surfaceWidth = update.state.icon.bitmap.width();
-            update.state.surfaceHeight = update.state.icon.bitmap.height();
+            update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width;
+            update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height;
             update.state.surfaceDrawn = false;
             update.state.surfaceVisible = false;
             update.state.surfaceControl = obtainSurface(
@@ -154,8 +152,8 @@
         }
 
         if (update.state.wantSurfaceVisible()) {
-            int32_t desiredWidth = update.state.icon.bitmap.width();
-            int32_t desiredHeight = update.state.icon.bitmap.height();
+            int32_t desiredWidth = update.state.icon.bitmap.getInfo().width;
+            int32_t desiredHeight = update.state.icon.bitmap.getInfo().height;
             if (update.state.surfaceWidth < desiredWidth
                     || update.state.surfaceHeight < desiredHeight) {
                 needApplyTransaction = true;
@@ -201,26 +199,22 @@
             if (status) {
                 ALOGE("Error %d locking sprite surface before drawing.", status);
             } else {
-                SkBitmap surfaceBitmap;
-                ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-                surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
-                                            outBuffer.bits, bpr);
+                graphics::Paint paint;
+                paint.setBlendMode(ABLEND_MODE_SRC);
 
-                SkCanvas surfaceCanvas(surfaceBitmap);
+                graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace());
+                canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
 
-                SkPaint paint;
-                paint.setBlendMode(SkBlendMode::kSrc);
-                surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
+                const int iconWidth = update.state.icon.bitmap.getInfo().width;
+                const int iconHeight = update.state.icon.bitmap.getInfo().height;
 
-                if (outBuffer.width > update.state.icon.bitmap.width()) {
-                    paint.setColor(0); // transparent fill color
-                    surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0,
-                            outBuffer.width, update.state.icon.bitmap.height()), paint);
+                if (outBuffer.width > iconWidth) {
+                    paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
+                    canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint);
                 }
-                if (outBuffer.height > update.state.icon.bitmap.height()) {
-                    paint.setColor(0); // transparent fill color
-                    surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(),
-                            outBuffer.width, outBuffer.height), paint);
+                if (outBuffer.height > iconHeight) {
+                    paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
+                    canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint);
                 }
 
                 status = surface->unlockAndPost();
@@ -398,12 +392,7 @@
 
     uint32_t dirty;
     if (icon.isValid()) {
-        SkBitmap* bitmapCopy = &mLocked.state.icon.bitmap;
-        if (bitmapCopy->tryAllocPixels(icon.bitmap.info().makeColorType(kN32_SkColorType))) {
-            icon.bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
-                    bitmapCopy->rowBytes(), 0, 0);
-        }
-
+        mLocked.state.icon.bitmap = icon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888);
         if (!mLocked.state.icon.isValid()
                 || mLocked.state.icon.hotSpotX != icon.hotSpotX
                 || mLocked.state.icon.hotSpotY != icon.hotSpotY) {
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 79a904f..2513544 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -20,10 +20,9 @@
 #include <utils/RefBase.h>
 #include <utils/Looper.h>
 
+#include <android/graphics/bitmap.h>
 #include <gui/SurfaceComposerClient.h>
 
-#include <SkBitmap.h>
-
 namespace android {
 
 /*
@@ -56,21 +55,16 @@
  */
 struct SpriteIcon {
     inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
-    inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+    inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
             bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
 
-    SkBitmap bitmap;
+    graphics::Bitmap bitmap;
     int32_t style;
     float hotSpotX;
     float hotSpotY;
 
     inline SpriteIcon copy() const {
-        SkBitmap bitmapCopy;
-        if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) {
-            bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
-                    0, 0);
-        }
-        return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
+        return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY);
     }
 
     inline void reset() {
@@ -81,7 +75,7 @@
     }
 
     inline bool isValid() const {
-        return !bitmap.isNull() && !bitmap.empty();
+        return bitmap.isValid() && !bitmap.isEmpty();
     }
 };
 
@@ -183,7 +177,7 @@
      * This structure is designed so that it can be copied during updates so that
      * surfaces can be resized and redrawn without blocking the client by holding a lock
      * on the sprites for a long time.
-     * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+     * Note that the SpriteIcon holds a reference to a shared (and immutable) bitmap. */
     struct SpriteState {
         inline SpriteState() :
                 dirty(0), visible(false),
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e83b2a7..b1e3d6f 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -18,9 +18,9 @@
         "PointerController_test.cpp",
     ],
     shared_libs: [
+        "libandroid_runtime",
         "libinputservice",
         "libgui",
-        "libhwui",
         "libutils",
     ],
     static_libs: [
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 378064d..6257feb 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -34,6 +34,7 @@
         "libutils",
         "libbinder",
         "libmedia",
+        "libmedia_codeclist",
         "libmedia_jni_utils",
         "libmedia_omx",
         "libmediametrics",
diff --git a/mime/Android.bp b/mime/Android.bp
new file mode 100644
index 0000000..17bad74
--- /dev/null
+++ b/mime/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+    name: "mimemap",
+    visibility: [
+        "//cts/tests/tests/mimemap:__subpackages__",
+        "//frameworks/base:__subpackages__",
+    ],
+
+    srcs: [
+        "java/android/content/type/DefaultMimeMapFactory.java",
+    ],
+
+    java_resources: [
+        ":debian.mime.types",
+        ":android.mime.types",
+    ],
+
+    sdk_version: "core_platform",
+}
+
+filegroup {
+    name: "android.mime.types",
+    visibility: [
+        "//visibility:private",
+    ],
+    path: "java-res/",
+    srcs: [
+        "java-res/android.mime.types",
+    ],
+}
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
new file mode 100644
index 0000000..1ca912e
--- /dev/null
+++ b/mime/java-res/android.mime.types
@@ -0,0 +1,146 @@
+
+###############################################################################
+#
+# Android-specific MIME type <-> extension mappings
+#
+# Each line below defines an mapping from one MIME type to the first of the
+# listed extensions, and from listed extension back to the MIME type.
+# A mapping overrides any previous mapping _from_ that same MIME type or
+# extension (put() semantics), unless that MIME type / extension is prefixed with '?'
+# (putIfAbsent() semantics).
+#
+#
+###############################################################################
+#
+# EXAMPLES
+#
+# A line of the form:
+#
+#    ?mime ext1 ?ext2 ext3
+#
+# affects the current mappings along the lines of the following pseudo code:
+#
+#    mimeToExt.putIfAbsent("mime", "ext1");
+#    extToMime.put("ext1", "mime");
+#    extToMime.putIfAbsent("ext2", "mime");
+#    extToMime.put("ext3", "mime");
+#
+# The line:
+#
+#     ?text/plain txt
+#
+# leaves any earlier mapping for "text/plain" untouched, or maps that MIME type
+# to the file extension ".txt" if there is no earlier mapping. The line also
+# sets the mapping from file extension ".txt" to be the MIME type "text/plain",
+# regardless of whether a previous mapping existed.
+#
+###############################################################################
+
+
+# File extensions that Android wants to override to point to the given MIME type.
+#
+# After processing a line of the form:
+# ?<mimeType> <extension1> <extension2>
+# If <mimeType> was not already mapped to an extension then it will be
+# mapped to <extension1>.
+# <extension1> and <extension2> are mapped (or remapped) to <mimeType>.
+
+?application/epub+zip epub
+?application/pkix-cert cer
+?application/rss+xml rss
+?application/vnd.android.ota ota
+?application/vnd.apple.mpegurl m3u8
+?application/vnd.ms-pki.stl stl
+?application/vnd.ms-powerpoint pot
+?application/vnd.ms-wpl wpl
+?application/vnd.stardivision.impress sdp
+?application/vnd.stardivision.writer vor
+?application/vnd.youtube.yt yt
+?application/x-android-drm-fl fl
+?application/x-flac flac
+?application/x-font pcf
+?application/x-mpegurl m3u m3u8
+?application/x-pem-file pem
+?application/x-pkcs12 p12 pfx
+?application/x-webarchive webarchive
+?application/x-webarchive-xml webarchivexml
+?application/x-x509-server-cert crt
+?application/x-x509-user-cert crt
+
+?audio/3gpp 3gpp
+?audio/aac-adts aac
+?audio/imelody imy
+?audio/midi rtttl xmf
+?audio/mobile-xmf mxmf
+?audio/mp4 m4a
+?audio/mpegurl m3u
+?audio/sp-midi smf
+?audio/x-matroska mka
+?audio/x-pn-realaudio ra
+
+?image/bmp bmp
+?image/heic heic
+?image/heic-sequence heics
+?image/heif heif hif
+?image/heif-sequence heifs
+?image/ico cur
+?image/webp webp
+?image/x-adobe-dng dng
+?image/x-fuji-raf raf
+?image/x-icon ico
+?image/x-nikon-nrw nrw
+?image/x-panasonic-rw2 rw2
+?image/x-pentax-pef pef
+?image/x-samsung-srw srw
+?image/x-sony-arw arw
+
+?text/comma-separated-values csv
+?text/plain diff po
+?text/rtf rtf
+?text/text phps
+?text/xml xml
+?text/x-vcard vcf
+
+?video/3gpp2 3gpp2 3g2
+?video/3gpp 3gpp
+?video/avi avi
+?video/m4v m4v
+?video/mp2p mpeg
+?video/mp2t m2ts mts
+?video/mp2ts ts
+?video/vnd.youtube.yt yt
+?video/x-webex wrf
+
+# Optional additions that should not override any previous mapping.
+
+?application/x-wifi-config ?xml
+
+# Special cases where Android has a strong opinion about mappings, so we
+# define them very last and make them override in both directions (no "?").
+#
+# Lines here are of the form:
+# <mimeType> <extension1> <extension2> ...
+#
+# After processing each line,
+#   <mimeType> is mapped to <extension1>
+#   <extension1>, <extension2>, ... are all mapped to <mimeType>
+# This overrides any mappings for this <mimeType> / for these extensions
+# that may have been defined earlier.
+
+application/pgp-signature pgp
+application/x-x509-ca-cert crt
+audio/aac aac
+audio/basic snd
+audio/flac flac
+audio/midi rtx
+audio/mpeg mp3 m4a m4r
+audio/x-mpegurl m3u m3u8
+image/jpeg jpg
+image/x-ms-bmp bmp
+text/plain txt
+text/x-c++hdr hpp
+text/x-c++src cpp
+video/3gpp 3gpp
+video/mpeg mpeg
+video/quicktime mov
+video/x-matroska mkv
diff --git a/mime/java/android/content/type/DefaultMimeMapFactory.java b/mime/java/android/content/type/DefaultMimeMapFactory.java
new file mode 100644
index 0000000..545fb3c
--- /dev/null
+++ b/mime/java/android/content/type/DefaultMimeMapFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.type;
+
+import libcore.net.MimeMap;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Creates the framework default {@link MimeMap}, a bidirectional mapping
+ * between MIME types and file extensions.
+ *
+ * This default mapping is loaded from data files that start with some mappings
+ * recognized by IANA plus some custom extensions and overrides.
+ *
+ * @hide
+ */
+public class DefaultMimeMapFactory {
+
+    private DefaultMimeMapFactory() {
+    }
+
+    /**
+     * Creates and returns a new {@link MimeMap} instance that implements.
+     * Android's default mapping between MIME types and extensions.
+     */
+    public static MimeMap create() {
+        return parseFromResources("/mime.types", "/android.mime.types");
+    }
+
+    private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");
+
+    static MimeMap parseFromResources(String... resourceNames) {
+        MimeMap.Builder builder = MimeMap.builder();
+        for (String resourceName : resourceNames) {
+            parseTypes(builder, resourceName);
+        }
+        return builder.build();
+    }
+
+    private static void parseTypes(MimeMap.Builder builder, String resource) {
+        try (BufferedReader r = new BufferedReader(
+                new InputStreamReader(DefaultMimeMapFactory.class.getResourceAsStream(resource)))) {
+            String line;
+            while ((line = r.readLine()) != null) {
+                int commentPos = line.indexOf('#');
+                if (commentPos >= 0) {
+                    line = line.substring(0, commentPos);
+                }
+                line = line.trim();
+                if (line.isEmpty()) {
+                    continue;
+                }
+                List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line));
+                builder.put(specs.get(0), specs.subList(1, specs.size()));
+            }
+        } catch (IOException | RuntimeException e) {
+            throw new RuntimeException("Failed to parse " + resource, e);
+        }
+    }
+
+}
diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java
index ca8d5ac..cc46514 100644
--- a/opengl/java/android/opengl/GLUtils.java
+++ b/opengl/java/android/opengl/GLUtils.java
@@ -44,7 +44,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        int result = native_getInternalFormat(bitmap.getNativeInstance());
+        int result = native_getInternalFormat(bitmap);
         if (result < 0) {
             throw new IllegalArgumentException("Unknown internalformat");
         }
@@ -66,7 +66,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        int result = native_getType(bitmap.getNativeInstance());
+        int result = native_getType(bitmap);
         if (result < 0) {
             throw new IllegalArgumentException("Unknown type");
         }
@@ -103,8 +103,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        if (native_texImage2D(target, level, internalformat, bitmap.getNativeInstance(), -1,
-                border) != 0) {
+        if (native_texImage2D(target, level, internalformat, bitmap, -1, border) != 0) {
             throw new IllegalArgumentException("invalid Bitmap format");
         }
     }
@@ -130,8 +129,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        if (native_texImage2D(target, level, internalformat, bitmap.getNativeInstance(), type,
-              border) != 0) {
+        if (native_texImage2D(target, level, internalformat, bitmap, type, border) != 0) {
             throw new IllegalArgumentException("invalid Bitmap format");
         }
     }
@@ -153,7 +151,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        if (native_texImage2D(target, level, -1, bitmap.getNativeInstance(), -1, border) != 0) {
+        if (native_texImage2D(target, level, -1, bitmap, -1, border) != 0) {
             throw new IllegalArgumentException("invalid Bitmap format");
         }
     }
@@ -189,8 +187,7 @@
             throw new IllegalArgumentException("bitmap is recycled");
         }
         int type = getType(bitmap);
-        if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap.getNativeInstance(), -1,
-                type) != 0) {
+        if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, -1, type) != 0) {
             throw new IllegalArgumentException("invalid Bitmap format");
         }
     }
@@ -214,8 +211,7 @@
         if (bitmap.isRecycled()) {
             throw new IllegalArgumentException("bitmap is recycled");
         }
-        if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap.getNativeInstance(),
-                format, type) != 0) {
+        if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, format, type) != 0) {
             throw new IllegalArgumentException("invalid Bitmap format");
         }
     }
@@ -265,10 +261,10 @@
         }
     }
 
-    native private static int native_getInternalFormat(long bitmapHandle);
-    native private static int native_getType(long bitmapHandle);
-    native private static int native_texImage2D(int target, int level, int internalformat,
-            long bitmapHandle, int type, int border);
-    native private static int native_texSubImage2D(int target, int level, int xoffset, int yoffset,
-            long bitmapHandle, int format, int type);
+    private static native int native_getInternalFormat(Bitmap bitmap);
+    private static native int native_getType(Bitmap bitmap);
+    private static native int native_texImage2D(int target, int level, int internalformat,
+            Bitmap bitmap, int type, int border);
+    private static native int native_texSubImage2D(int target, int level, int xoffset, int yoffset,
+            Bitmap bitmap, int format, int type);
 }
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
new file mode 100644
index 0000000..f3ab2bde
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import android.app.backup.BackupTransport;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Accepts the full backup data stream and sends it to the server. */
+public interface FullBackupDataProcessor {
+    /**
+     * Prepares the upload.
+     *
+     * <p>After this, call {@link #start()} to establish the connection.
+     *
+     * @param inputStream to read the backup data from, calling {@link #finish} or {@link #cancel}
+     *     will close the stream
+     * @return {@code true} if the connection was set up successfully, otherwise {@code false}
+     */
+    boolean initiate(InputStream inputStream) throws IOException;
+
+    /**
+     * Starts the upload, establishing the connection to the server.
+     *
+     * <p>After this, call {@link #pushData(int)} to request that the processor reads data from the
+     * socket, and uploads it to the server.
+     *
+     * <p>After this you must call one of {@link #cancel()}, {@link #finish()}, {@link
+     * #handleCheckSizeRejectionZeroBytes()}, {@link #handleCheckSizeRejectionQuotaExceeded()} or
+     * {@link #handleSendBytesQuotaExceeded()} to close the upload.
+     */
+    void start();
+
+    /**
+     * Requests that the processor read {@code numBytes} from the input stream passed in {@link
+     * #initiate(InputStream)} and upload them to the server.
+     *
+     * @return {@link BackupTransport#TRANSPORT_OK} if the upload succeeds, or {@link
+     *     BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
+     *     quota, or {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
+     */
+    int pushData(int numBytes);
+
+    /** Cancels the upload and tears down the connection. */
+    void cancel();
+
+    /**
+     * Finish the upload and tear down the connection.
+     *
+     * <p>Call this after there is no more data to push with {@link #pushData(int)}.
+     *
+     * @return One of {@link BackupTransport#TRANSPORT_OK} if the app upload succeeds, {@link
+     *     BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
+     *     quota, {@link BackupTransport#TRANSPORT_ERROR} for server 500s, or {@link
+     *     BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
+     */
+    int finish();
+
+    /**
+     * Notifies the processor that the current upload should be terminated because the estimated
+     * size is zero.
+     */
+    void handleCheckSizeRejectionZeroBytes();
+
+    /**
+     * Notifies the processor that the current upload should be terminated because the estimated
+     * size exceeds the quota.
+     */
+    void handleCheckSizeRejectionQuotaExceeded();
+
+    /**
+     * Notifies this class that the current upload should be terminated because the quota was
+     * exceeded during upload.
+     */
+    void handleSendBytesQuotaExceeded();
+
+    /**
+     * Attaches {@link FullBackupCallbacks} which the processor will notify when the backup
+     * succeeds.
+     */
+    void attachCallbacks(FullBackupCallbacks fullBackupCallbacks);
+
+    /**
+     * Implemented by the caller of the processor to receive notification of when the backup
+     * succeeds.
+     */
+    interface FullBackupCallbacks {
+        /** The processor calls this to indicate that the current backup has succeeded. */
+        void onSuccess();
+
+        /** The processor calls this if the upload failed for a non-transient reason. */
+        void onTransferFailed();
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
new file mode 100644
index 0000000..e4c4049
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.IOException;
+
+/**
+ * Retrieves the data during a full restore, decrypting it if necessary.
+ *
+ * <p>Use {@link FullRestoreDataProcessorFactory} to construct the encrypted or unencrypted
+ * processor as appropriate during restore.
+ */
+public interface FullRestoreDataProcessor {
+    /** Return value of {@link #readNextChunk} when there is no more data to download. */
+    int END_OF_STREAM = -1;
+
+    /**
+     * Reads the next chunk of restore data and writes it to the given buffer.
+     *
+     * <p>Where necessary, will open the connection to the server and/or decrypt the backup file.
+     *
+     * <p>The implementation may retry various errors. If the retries fail it will throw the
+     * relevant exception.
+     *
+     * @return the number of bytes read, or {@link #END_OF_STREAM} if there is no more data
+     * @throws IOException when downloading from the network or writing to disk
+     */
+    int readNextChunk(byte[] buffer) throws IOException;
+
+    /**
+     * Closes the connection to the server, deletes any temporary files and optionally sends a log
+     * with the given finish type.
+     *
+     * @param finishType one of {@link FullRestoreDownloader.FinishType}
+     */
+    void finish(FullRestoreDownloader.FinishType finishType);
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
new file mode 100644
index 0000000..afcca79
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.IOException;
+
+/** Interface for classes which will provide backup data */
+public abstract class FullRestoreDownloader {
+    /** Enum to provide information on why a download finished */
+    public enum FinishType {
+        UNKNOWN_FINISH(0),
+        // Finish the downloading and successfully write data to Android OS.
+        FINISHED(1),
+        // Download failed with any kind of exception.
+        TRANSFER_FAILURE(2),
+        // Download failed due to auth failure on the device.
+        AUTH_FAILURE(3),
+        // Aborted by Android Framework.
+        FRAMEWORK_ABORTED(4);
+
+        private int mValue;
+
+        FinishType(int value) {
+            mValue = value;
+        }
+    }
+
+    /** Get the next data chunk from the backing store */
+    public abstract int readNextChunk(byte[] buffer) throws IOException;
+
+    /** Called when we've finished restoring the data */
+    public abstract void finish(FinishType finishType);
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
new file mode 100644
index 0000000..91b2926
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/** Utility methods for dealing with Streams */
+public class StreamUtils {
+    /**
+     * Close a Closeable and silently ignore any IOExceptions.
+     *
+     * @param closeable The closeable to close
+     */
+    public static void closeQuietly(Closeable closeable) {
+        try {
+            closeable.close();
+        } catch (IOException ioe) {
+            // Silently ignore
+        }
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
new file mode 100644
index 0000000..9e31385
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.client;
+
+/**
+ * Error thrown when the user attempts to retrieve a key set from the server, but is asking for keys
+ * from an inactive secondary.
+ *
+ * <p>Although we could just return old keys, there is no good reason to do this. It almost
+ * certainly indicates a logic error on the client.
+ */
+public class UnexpectedActiveSecondaryOnServerException extends Exception {
+    public UnexpectedActiveSecondaryOnServerException(String message) {
+        super(message);
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
new file mode 100644
index 0000000..2e8a61f
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown when the server's active secondary key does not exist in the user's recoverable
+ * keychain. This means the backup data cannot be decrypted, and should be wiped.
+ */
+public class ActiveSecondaryNotInKeychainException extends Exception {
+    public ActiveSecondaryNotInKeychainException(String message) {
+        super(message);
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
new file mode 100644
index 0000000..9bf148d
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * A backup file consists of, in order:
+ *
+ * <ul>
+ *   <li>A randomly ordered sequence of encrypted chunks
+ *   <li>A plaintext {@link ChunksMetadata} proto, containing the bytes of an encrypted {@link
+ *       ChunkOrdering} proto.
+ *   <li>A 64-bit long denoting the offset of the file at which the ChunkOrdering proto starts.
+ * </ul>
+ *
+ * <p>This task decrypts such a blob and writes the plaintext to another file.
+ *
+ * <p>The backup file has two formats to indicate the boundaries of the chunks in the encrypted
+ * file. In {@link ChunksMetadataProto#EXPLICIT_STARTS} mode the chunk ordering contains the start
+ * positions of each chunk and the decryptor outputs the chunks in the order they appeared in the
+ * plaintext file. In {@link ChunksMetadataProto#INLINE_LENGTHS} mode the length of each encrypted
+ * chunk is prepended to the chunk in the file and the decryptor outputs the chunks in no specific
+ * order.
+ *
+ * <p>{@link ChunksMetadataProto#EXPLICIT_STARTS} is for use with full backup (Currently used for
+ * all backups as b/77188289 is not implemented yet), {@link ChunksMetadataProto#INLINE_LENGTHS}
+ * will be used for kv backup (once b/77188289 is implemented) to avoid re-uploading the chunk
+ * ordering (see b/70782620).
+ */
+public class BackupFileDecryptorTask {
+    private static final String TAG = "BackupFileDecryptorTask";
+
+    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+    private static final int GCM_NONCE_LENGTH_BYTES = 12;
+    private static final int GCM_TAG_LENGTH_BYTES = 16;
+    private static final int BITS_PER_BYTE = 8;
+    private static final String READ_MODE = "r";
+    private static final int BYTES_PER_LONG = 64 / BITS_PER_BYTE;
+
+    private final Cipher mCipher;
+    private final SecretKey mSecretKey;
+
+    /**
+     * A new instance.
+     *
+     * @param secretKey The tertiary key used to encrypt the backup blob.
+     */
+    public BackupFileDecryptorTask(SecretKey secretKey)
+            throws NoSuchPaddingException, NoSuchAlgorithmException {
+        this.mCipher = Cipher.getInstance(CIPHER_ALGORITHM);
+        this.mSecretKey = secretKey;
+    }
+
+    /**
+     * Runs the task, reading the encrypted data from {@code input} and writing the plaintext data
+     * to {@code output}.
+     *
+     * @param inputFile The encrypted backup file.
+     * @param decryptedChunkOutput Unopened output to write the plaintext to, which this class will
+     *     open and close during decryption.
+     * @throws IOException if an error occurred reading the encrypted file or writing the plaintext,
+     *     or if one of the protos could not be deserialized.
+     */
+    public void decryptFile(File inputFile, DecryptedChunkOutput decryptedChunkOutput)
+            throws IOException, EncryptedRestoreException, IllegalBlockSizeException,
+                    BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException,
+                    ShortBufferException, NoSuchAlgorithmException {
+        RandomAccessFile input = new RandomAccessFile(inputFile, READ_MODE);
+
+        long metadataOffset = getChunksMetadataOffset(input);
+        ChunksMetadataProto.ChunksMetadata chunksMetadata =
+                getChunksMetadata(input, metadataOffset);
+        ChunkOrdering chunkOrdering = decryptChunkOrdering(chunksMetadata);
+
+        if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED
+                || chunksMetadata.chunkOrderingType == ChunksMetadataProto.EXPLICIT_STARTS) {
+            Slog.d(TAG, "Using explicit starts");
+            decryptFileWithExplicitStarts(
+                    input, decryptedChunkOutput, chunkOrdering, metadataOffset);
+
+        } else if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.INLINE_LENGTHS) {
+            Slog.d(TAG, "Using inline lengths");
+            decryptFileWithInlineLengths(input, decryptedChunkOutput, metadataOffset);
+
+        } else {
+            throw new UnsupportedEncryptedFileException(
+                    "Unknown chunk ordering type:" + chunksMetadata.chunkOrderingType);
+        }
+
+        if (!Arrays.equals(decryptedChunkOutput.getDigest(), chunkOrdering.checksum)) {
+            throw new MessageDigestMismatchException("Checksums did not match");
+        }
+    }
+
+    private void decryptFileWithExplicitStarts(
+            RandomAccessFile input,
+            DecryptedChunkOutput decryptedChunkOutput,
+            ChunkOrdering chunkOrdering,
+            long metadataOffset)
+            throws IOException, InvalidKeyException, IllegalBlockSizeException,
+                    InvalidAlgorithmParameterException, ShortBufferException, BadPaddingException,
+                    NoSuchAlgorithmException {
+        SparseIntArray chunkLengthsByPosition =
+                getChunkLengths(chunkOrdering.starts, (int) metadataOffset);
+        int largestChunkLength = getLargestChunkLength(chunkLengthsByPosition);
+        byte[] encryptedChunkBuffer = new byte[largestChunkLength];
+        // largestChunkLength is 0 if the backup file contains zero chunks e.g. 0 kv pairs.
+        int plaintextBufferLength =
+                Math.max(0, largestChunkLength - GCM_NONCE_LENGTH_BYTES - GCM_TAG_LENGTH_BYTES);
+        byte[] plaintextChunkBuffer = new byte[plaintextBufferLength];
+
+        try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
+            for (int start : chunkOrdering.starts) {
+                int length = chunkLengthsByPosition.get(start);
+
+                input.seek(start);
+                input.readFully(encryptedChunkBuffer, 0, length);
+                int plaintextLength =
+                        decryptChunk(encryptedChunkBuffer, length, plaintextChunkBuffer);
+                outputChunk(output, plaintextChunkBuffer, plaintextLength);
+            }
+        }
+    }
+
+    private void decryptFileWithInlineLengths(
+            RandomAccessFile input, DecryptedChunkOutput decryptedChunkOutput, long metadataOffset)
+            throws MalformedEncryptedFileException, IOException, IllegalBlockSizeException,
+                    BadPaddingException, InvalidAlgorithmParameterException, ShortBufferException,
+                    InvalidKeyException, NoSuchAlgorithmException {
+        input.seek(0);
+        try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
+            while (input.getFilePointer() < metadataOffset) {
+                long start = input.getFilePointer();
+                int encryptedChunkLength = input.readInt();
+
+                if (encryptedChunkLength <= 0) {
+                    // If the length of the encrypted chunk is not positive we will not make
+                    // progress reading the file and so will loop forever.
+                    throw new MalformedEncryptedFileException(
+                            "Encrypted chunk length not positive:" + encryptedChunkLength);
+                }
+
+                if (start + encryptedChunkLength > metadataOffset) {
+                    throw new MalformedEncryptedFileException(
+                            String.format(
+                                    Locale.US,
+                                    "Encrypted chunk longer (%d) than file (%d)",
+                                    encryptedChunkLength,
+                                    metadataOffset));
+                }
+
+                byte[] plaintextChunk = new byte[encryptedChunkLength];
+                byte[] plaintext =
+                        new byte
+                                [encryptedChunkLength
+                                        - GCM_NONCE_LENGTH_BYTES
+                                        - GCM_TAG_LENGTH_BYTES];
+
+                input.readFully(plaintextChunk);
+
+                int plaintextChunkLength =
+                        decryptChunk(plaintextChunk, encryptedChunkLength, plaintext);
+                outputChunk(output, plaintext, plaintextChunkLength);
+            }
+        }
+    }
+
+    private void outputChunk(
+            DecryptedChunkOutput output, byte[] plaintextChunkBuffer, int plaintextLength)
+            throws IOException, InvalidKeyException, NoSuchAlgorithmException {
+        output.processChunk(plaintextChunkBuffer, plaintextLength);
+    }
+
+    /**
+     * Decrypts chunk and returns the length of the plaintext.
+     *
+     * @param encryptedChunkBuffer The encrypted data, prefixed by the nonce.
+     * @param encryptedChunkBufferLength The length of the encrypted chunk (including nonce).
+     * @param plaintextChunkBuffer The buffer into which to write the plaintext chunk.
+     * @return The length of the plaintext chunk.
+     */
+    private int decryptChunk(
+            byte[] encryptedChunkBuffer,
+            int encryptedChunkBufferLength,
+            byte[] plaintextChunkBuffer)
+            throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
+                    ShortBufferException, IllegalBlockSizeException {
+
+        mCipher.init(
+                Cipher.DECRYPT_MODE,
+                mSecretKey,
+                new GCMParameterSpec(
+                        GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
+                        encryptedChunkBuffer,
+                        0,
+                        GCM_NONCE_LENGTH_BYTES));
+
+        return mCipher.doFinal(
+                encryptedChunkBuffer,
+                GCM_NONCE_LENGTH_BYTES,
+                encryptedChunkBufferLength - GCM_NONCE_LENGTH_BYTES,
+                plaintextChunkBuffer);
+    }
+
+    /** Given all the lengths, returns the largest length. */
+    private int getLargestChunkLength(SparseIntArray lengths) {
+        int maxSeen = 0;
+        for (int i = 0; i < lengths.size(); i++) {
+            maxSeen = Math.max(maxSeen, lengths.valueAt(i));
+        }
+        return maxSeen;
+    }
+
+    /**
+     * From a list of the starting position of each chunk in the correct order of the backup data,
+     * calculates a mapping from start position to length of that chunk.
+     *
+     * @param starts The start positions of chunks, in order.
+     * @param chunkOrderingPosition Where the {@link ChunkOrdering} proto starts, used to calculate
+     *     the length of the last chunk.
+     * @return The mapping.
+     */
+    private SparseIntArray getChunkLengths(int[] starts, int chunkOrderingPosition) {
+        int[] boundaries = Arrays.copyOf(starts, starts.length + 1);
+        boundaries[boundaries.length - 1] = chunkOrderingPosition;
+        Arrays.sort(boundaries);
+
+        SparseIntArray lengths = new SparseIntArray();
+        for (int i = 0; i < boundaries.length - 1; i++) {
+            lengths.put(boundaries[i], boundaries[i + 1] - boundaries[i]);
+        }
+        return lengths;
+    }
+
+    /**
+     * Reads and decrypts the {@link ChunkOrdering} from the {@link ChunksMetadata}.
+     *
+     * @param metadata The metadata.
+     * @return The ordering.
+     * @throws InvalidProtocolBufferNanoException if there is an issue deserializing the proto.
+     */
+    private ChunkOrdering decryptChunkOrdering(ChunksMetadata metadata)
+            throws InvalidProtocolBufferNanoException, InvalidAlgorithmParameterException,
+                    InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
+                    UnsupportedEncryptedFileException {
+        assertCryptoSupported(metadata);
+
+        mCipher.init(
+                Cipher.DECRYPT_MODE,
+                mSecretKey,
+                new GCMParameterSpec(
+                        GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
+                        metadata.chunkOrdering,
+                        0,
+                        GCM_NONCE_LENGTH_BYTES));
+
+        byte[] decrypted =
+                mCipher.doFinal(
+                        metadata.chunkOrdering,
+                        GCM_NONCE_LENGTH_BYTES,
+                        metadata.chunkOrdering.length - GCM_NONCE_LENGTH_BYTES);
+
+        return ChunkOrdering.parseFrom(decrypted);
+    }
+
+    /**
+     * Asserts that the Cipher and MessageDigest algorithms in the backup metadata are supported.
+     * For now we only support SHA-256 for checksum and 256-bit AES/GCM/NoPadding for the Cipher.
+     *
+     * @param chunksMetadata The file metadata.
+     * @throws UnsupportedEncryptedFileException if any algorithm is unsupported.
+     */
+    private void assertCryptoSupported(ChunksMetadata chunksMetadata)
+            throws UnsupportedEncryptedFileException {
+        if (chunksMetadata.checksumType != ChunksMetadataProto.SHA_256) {
+            // For now we only support SHA-256.
+            throw new UnsupportedEncryptedFileException(
+                    "Unrecognized checksum type for backup (this version of backup only supports"
+                        + " SHA-256): "
+                            + chunksMetadata.checksumType);
+        }
+
+        if (chunksMetadata.cipherType != ChunksMetadataProto.AES_256_GCM) {
+            throw new UnsupportedEncryptedFileException(
+                    "Unrecognized cipher type for backup (this version of backup only supports"
+                        + " AES-256-GCM: "
+                            + chunksMetadata.cipherType);
+        }
+    }
+
+    /**
+     * Reads the offset of the {@link ChunksMetadata} proto from the end of the file.
+     *
+     * @return The offset.
+     * @throws IOException if there is an error reading.
+     */
+    private long getChunksMetadataOffset(RandomAccessFile input) throws IOException {
+        input.seek(input.length() - BYTES_PER_LONG);
+        return input.readLong();
+    }
+
+    /**
+     * Reads the {@link ChunksMetadata} proto from the given position in the file.
+     *
+     * @param input The encrypted file.
+     * @param position The position where the proto starts.
+     * @return The proto.
+     * @throws IOException if there is an issue reading the file or deserializing the proto.
+     */
+    private ChunksMetadata getChunksMetadata(RandomAccessFile input, long position)
+            throws IOException, MalformedEncryptedFileException {
+        long length = input.length();
+        if (position >= length || position < 0) {
+            throw new MalformedEncryptedFileException(
+                    String.format(
+                            Locale.US,
+                            "%d is not valid position for chunks metadata in file of %d bytes",
+                            position,
+                            length));
+        }
+
+        // Read chunk ordering bytes
+        input.seek(position);
+        long chunksMetadataLength = input.length() - BYTES_PER_LONG - position;
+        byte[] chunksMetadataBytes = new byte[(int) chunksMetadataLength];
+        input.readFully(chunksMetadataBytes);
+
+        try {
+            return ChunksMetadata.parseFrom(chunksMetadataBytes);
+        } catch (InvalidProtocolBufferNanoException e) {
+            throw new MalformedEncryptedFileException(
+                    String.format(
+                            Locale.US,
+                            "Could not read chunks metadata at position %d of file of %d bytes",
+                            position,
+                            length));
+        }
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
new file mode 100644
index 0000000..a938d71
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Task which reads a stream of plaintext full backup data, chunks it, encrypts it and uploads it to
+ * the server.
+ *
+ * <p>Once the backup completes or fails, closes the input stream.
+ */
+public class EncryptedFullBackupTask implements Callable<Void> {
+    private static final String TAG = "EncryptedFullBackupTask";
+
+    private static final int MIN_CHUNK_SIZE_BYTES = 2 * 1024;
+    private static final int MAX_CHUNK_SIZE_BYTES = 64 * 1024;
+    private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * 1024;
+
+    // TODO(b/69350270): Remove this hard-coded salt and related logic once we feel confident that
+    // incremental backup has happened at least once for all existing packages/users since we moved
+    // to
+    // using a randomly generated salt.
+    //
+    // The hard-coded fingerprint mixer salt was used for a short time period before replaced by one
+    // that is randomly generated on initial non-incremental backup and stored in ChunkListing to be
+    // reused for succeeding incremental backups. If an old ChunkListing does not have a
+    // fingerprint_mixer_salt, we assume that it was last backed up before a randomly generated salt
+    // is used so we use the hardcoded salt and set ChunkListing#fingerprint_mixer_salt to this
+    // value.
+    // Eventually all backup ChunkListings will have this field set and then we can remove the
+    // default
+    // value in the code.
+    static final byte[] DEFAULT_FINGERPRINT_MIXER_SALT =
+            Arrays.copyOf(new byte[] {20, 23}, FingerprintMixer.SALT_LENGTH_BYTES);
+
+    private final ProtoStore<ChunkListing> mChunkListingStore;
+    private final TertiaryKeyManager mTertiaryKeyManager;
+    private final InputStream mInputStream;
+    private final EncryptedBackupTask mTask;
+    private final String mPackageName;
+    private final SecureRandom mSecureRandom;
+
+    /** Creates a new instance with the default min, max and average chunk sizes. */
+    public static EncryptedFullBackupTask newInstance(
+            Context context,
+            CryptoBackupServer cryptoBackupServer,
+            SecureRandom secureRandom,
+            RecoverableKeyStoreSecondaryKey secondaryKey,
+            String packageName,
+            InputStream inputStream)
+            throws IOException {
+        EncryptedBackupTask encryptedBackupTask =
+                new EncryptedBackupTask(
+                        cryptoBackupServer,
+                        secureRandom,
+                        packageName,
+                        new BackupStreamEncrypter(
+                                inputStream,
+                                MIN_CHUNK_SIZE_BYTES,
+                                MAX_CHUNK_SIZE_BYTES,
+                                AVERAGE_CHUNK_SIZE_BYTES));
+        TertiaryKeyManager tertiaryKeyManager =
+                new TertiaryKeyManager(
+                        context,
+                        secureRandom,
+                        TertiaryKeyRotationScheduler.getInstance(context),
+                        secondaryKey,
+                        packageName);
+
+        return new EncryptedFullBackupTask(
+                ProtoStore.createChunkListingStore(context),
+                tertiaryKeyManager,
+                encryptedBackupTask,
+                inputStream,
+                packageName,
+                new SecureRandom());
+    }
+
+    @VisibleForTesting
+    EncryptedFullBackupTask(
+            ProtoStore<ChunkListing> chunkListingStore,
+            TertiaryKeyManager tertiaryKeyManager,
+            EncryptedBackupTask task,
+            InputStream inputStream,
+            String packageName,
+            SecureRandom secureRandom) {
+        mChunkListingStore = chunkListingStore;
+        mTertiaryKeyManager = tertiaryKeyManager;
+        mInputStream = inputStream;
+        mTask = task;
+        mPackageName = packageName;
+        mSecureRandom = secureRandom;
+    }
+
+    @Override
+    public Void call() throws Exception {
+        try {
+            Optional<ChunkListing> maybeOldChunkListing =
+                    mChunkListingStore.loadProto(mPackageName);
+
+            if (maybeOldChunkListing.isPresent()) {
+                Slog.i(TAG, "Found previous chunk listing for " + mPackageName);
+            }
+
+            // If the key has been rotated then we must re-encrypt all of the backup data.
+            if (mTertiaryKeyManager.wasKeyRotated()) {
+                Slog.i(
+                        TAG,
+                        "Key was rotated or newly generated for "
+                                + mPackageName
+                                + ", so performing a full backup.");
+                maybeOldChunkListing = Optional.empty();
+                mChunkListingStore.deleteProto(mPackageName);
+            }
+
+            SecretKey tertiaryKey = mTertiaryKeyManager.getKey();
+            WrappedKeyProto.WrappedKey wrappedTertiaryKey = mTertiaryKeyManager.getWrappedKey();
+
+            ChunkListing newChunkListing;
+            if (!maybeOldChunkListing.isPresent()) {
+                byte[] fingerprintMixerSalt = new byte[FingerprintMixer.SALT_LENGTH_BYTES];
+                mSecureRandom.nextBytes(fingerprintMixerSalt);
+                newChunkListing =
+                        mTask.performNonIncrementalBackup(
+                                tertiaryKey, wrappedTertiaryKey, fingerprintMixerSalt);
+            } else {
+                ChunkListing oldChunkListing = maybeOldChunkListing.get();
+
+                if (oldChunkListing.fingerprintMixerSalt == null
+                        || oldChunkListing.fingerprintMixerSalt.length == 0) {
+                    oldChunkListing.fingerprintMixerSalt = DEFAULT_FINGERPRINT_MIXER_SALT;
+                }
+
+                newChunkListing =
+                        mTask.performIncrementalBackup(
+                                tertiaryKey, wrappedTertiaryKey, oldChunkListing);
+            }
+
+            mChunkListingStore.saveProto(mPackageName, newChunkListing);
+            Slog.v(TAG, "Saved chunk listing for " + mPackageName);
+        } catch (IOException e) {
+            Slog.e(TAG, "Storage exception, wiping state");
+            mChunkListingStore.deleteProto(mPackageName);
+            throw e;
+        } finally {
+            StreamUtils.closeQuietly(mInputStream);
+        }
+
+        return null;
+    }
+
+    /**
+     * Signals to the task that the backup has been cancelled. If the upload has not yet started
+     * then the task will not upload any data to the server or save the new chunk listing.
+     *
+     * <p>You must then terminate the input stream.
+     */
+    public void cancel() {
+        mTask.cancel();
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
new file mode 100644
index 0000000..04381af
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.Nullable;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.FullRestoreDataProcessor;
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/** Downloads the encrypted backup file, decrypts it and passes the data to backup manager. */
+public class EncryptedFullRestoreTask implements FullRestoreDataProcessor {
+    private static final String DEFAULT_TEMPORARY_FOLDER = "encrypted_restore_temp";
+    private static final String ENCRYPTED_FILE_NAME = "encrypted_restore";
+    private static final String DECRYPTED_FILE_NAME = "decrypted_restore";
+
+    private final FullRestoreToFileTask mFullRestoreToFileTask;
+    private final BackupFileDecryptorTask mBackupFileDecryptorTask;
+    private final File mEncryptedFile;
+    private final File mDecryptedFile;
+    @Nullable private InputStream mDecryptedFileInputStream;
+
+    /**
+     * Creates a new task which stores temporary files in the files directory.
+     *
+     * @param fullRestoreDownloader which will download the backup file
+     * @param tertiaryKey which the backup file is encrypted with
+     */
+    public static EncryptedFullRestoreTask newInstance(
+            Context context, FullRestoreDownloader fullRestoreDownloader, SecretKey tertiaryKey)
+            throws NoSuchAlgorithmException, NoSuchPaddingException {
+        File temporaryFolder = new File(context.getFilesDir(), DEFAULT_TEMPORARY_FOLDER);
+        temporaryFolder.mkdirs();
+        return new EncryptedFullRestoreTask(
+                temporaryFolder, fullRestoreDownloader, new BackupFileDecryptorTask(tertiaryKey));
+    }
+
+    @VisibleForTesting
+    EncryptedFullRestoreTask(
+            File temporaryFolder,
+            FullRestoreDownloader fullRestoreDownloader,
+            BackupFileDecryptorTask backupFileDecryptorTask) {
+        checkArgument(temporaryFolder.isDirectory(), "Temporary folder must be existing directory");
+
+        mEncryptedFile = new File(temporaryFolder, ENCRYPTED_FILE_NAME);
+        mDecryptedFile = new File(temporaryFolder, DECRYPTED_FILE_NAME);
+
+        mFullRestoreToFileTask = new FullRestoreToFileTask(fullRestoreDownloader);
+        mBackupFileDecryptorTask = backupFileDecryptorTask;
+    }
+
+    /**
+     * Reads the next decrypted bytes into the given buffer.
+     *
+     * <p>During the first call this method will download the backup file from the server, decrypt
+     * it and save it to disk. It will then read the bytes from the file on disk.
+     *
+     * <p>Once this method has read all the bytes of the file, the caller must call {@link #finish}
+     * to clean up.
+     *
+     * @return the number of bytes read, or {@code -1} on reaching the end of the file
+     */
+    @Override
+    public int readNextChunk(byte[] buffer) throws IOException {
+        if (mDecryptedFileInputStream == null) {
+            try {
+                mDecryptedFileInputStream = downloadAndDecryptBackup();
+            } catch (BadPaddingException
+                    | InvalidKeyException
+                    | NoSuchAlgorithmException
+                    | IllegalBlockSizeException
+                    | ShortBufferException
+                    | EncryptedRestoreException
+                    | InvalidAlgorithmParameterException e) {
+                throw new IOException("Encryption issue", e);
+            }
+        }
+
+        return mDecryptedFileInputStream.read(buffer);
+    }
+
+    private InputStream downloadAndDecryptBackup()
+            throws IOException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
+                    IllegalBlockSizeException, ShortBufferException, EncryptedRestoreException,
+                    InvalidAlgorithmParameterException {
+        mFullRestoreToFileTask.restoreToFile(mEncryptedFile);
+        mBackupFileDecryptorTask.decryptFile(
+                mEncryptedFile, new DecryptedChunkFileOutput(mDecryptedFile));
+        mEncryptedFile.delete();
+        return new BufferedInputStream(new FileInputStream(mDecryptedFile));
+    }
+
+    /** Cleans up temporary files. */
+    @Override
+    public void finish(FullRestoreDownloader.FinishType unusedFinishType) {
+        // The download is finished and log sent during RestoreToFileTask#restoreToFile(), so we
+        // don't need to do either of those things here.
+
+        StreamUtils.closeQuietly(mDecryptedFileInputStream);
+        mEncryptedFile.delete();
+        mDecryptedFile.delete();
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
new file mode 100644
index 0000000..82f83f9
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Reads a stream from a {@link FullRestoreDownloader} and writes it to a file for consumption by
+ * {@link BackupFileDecryptorTask}.
+ */
+public class FullRestoreToFileTask {
+    /**
+     * Maximum number of bytes which the framework can request from the full restore data stream in
+     * one call to {@link BackupTransport#getNextFullRestoreDataChunk}.
+     */
+    public static final int MAX_BYTES_FULL_RESTORE_CHUNK = 1024 * 32;
+
+    /** Returned when the end of a backup stream has been reached. */
+    private static final int END_OF_STREAM = -1;
+
+    private final FullRestoreDownloader mFullRestoreDownloader;
+    private final int mBufferSize;
+
+    /**
+     * Constructs a new instance which reads from the given package wrapper, using a buffer of size
+     * {@link #MAX_BYTES_FULL_RESTORE_CHUNK}.
+     */
+    public FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader) {
+        this(fullRestoreDownloader, MAX_BYTES_FULL_RESTORE_CHUNK);
+    }
+
+    @VisibleForTesting
+    FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader, int bufferSize) {
+        checkArgument(bufferSize > 0, "Buffer must have positive size");
+
+        this.mFullRestoreDownloader = fullRestoreDownloader;
+        this.mBufferSize = bufferSize;
+    }
+
+    /**
+     * Downloads the backup file from the server and writes it to the given file.
+     *
+     * <p>At the end of the download (success or failure), closes the connection and sends a
+     * Clearcut log.
+     */
+    public void restoreToFile(File targetFile) throws IOException {
+        try (BufferedOutputStream outputStream =
+                new BufferedOutputStream(new FileOutputStream(targetFile))) {
+            byte[] buffer = new byte[mBufferSize];
+            int bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
+            while (bytesRead != END_OF_STREAM) {
+                outputStream.write(buffer, /* off=*/ 0, bytesRead);
+                bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
+            }
+
+            outputStream.flush();
+
+            mFullRestoreDownloader.finish(FinishType.FINISHED);
+        } catch (IOException e) {
+            mFullRestoreDownloader.finish(FinishType.TRANSFER_FAILURE);
+            throw e;
+        }
+    }
+}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
similarity index 62%
copy from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
copy to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
index 1e721aa..78c370b 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,19 +12,13 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package com.android.frameworks.coretests;
+package com.android.server.backup.encryption.tasks;
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class SecondChildTestService extends Service {
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
+/** Exception thrown when we cannot parse the encrypted backup file. */
+public class MalformedEncryptedFileException extends EncryptedRestoreException {
+    public MalformedEncryptedFileException(String message) {
+        super(message);
     }
 }
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
new file mode 100644
index 0000000..1e4f43b
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown if the message digest of the plaintext backup does not match that in the {@link
+ * com.android.server.backup.encryption.protos.ChunksMetadataProto.ChunkOrdering}.
+ */
+public class MessageDigestMismatchException extends EncryptedRestoreException {
+    public MessageDigestMismatchException(String message) {
+        super(message);
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
new file mode 100644
index 0000000..72e8a89
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown if attempting to rotate key when there is no current active secondary key set
+ * locally. This means the device needs to re-initialize, asking the backup server what the active
+ * secondary key is.
+ */
+public class NoActiveSecondaryKeyException extends Exception {
+    public NoActiveSecondaryKeyException(String message) {
+        super(message);
+    }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
new file mode 100644
index 0000000..d58cb66
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyStore;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Finishes a rotation for a {@link
+ * com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey}.
+ */
+public class RotateSecondaryKeyTask {
+    private static final String TAG = "RotateSecondaryKeyTask";
+
+    private final Context mContext;
+    private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+    private final CryptoBackupServer mBackupServer;
+    private final CryptoSettings mCryptoSettings;
+    private final RecoveryController mRecoveryController;
+
+    /**
+     * A new instance.
+     *
+     * @param secondaryKeyManager For loading the currently active and next secondary key.
+     * @param backupServer For loading and storing tertiary keys and for setting active secondary
+     *     key.
+     * @param cryptoSettings For checking the stored aliases for the next and active key.
+     * @param recoveryController For communicating with the Framework apis.
+     */
+    public RotateSecondaryKeyTask(
+            Context context,
+            RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
+            CryptoBackupServer backupServer,
+            CryptoSettings cryptoSettings,
+            RecoveryController recoveryController) {
+        mContext = context;
+        mSecondaryKeyManager = checkNotNull(secondaryKeyManager);
+        mCryptoSettings = checkNotNull(cryptoSettings);
+        mBackupServer = checkNotNull(backupServer);
+        mRecoveryController = checkNotNull(recoveryController);
+    }
+
+    /** Runs the task. */
+    public void run() {
+        // Never run more than one of these at the same time.
+        synchronized (RotateSecondaryKeyTask.class) {
+            runInternal();
+        }
+    }
+
+    private void runInternal() {
+        Optional<RecoverableKeyStoreSecondaryKey> maybeNextKey;
+        try {
+            maybeNextKey = getNextKey();
+        } catch (Exception e) {
+            Slog.e(TAG, "Error checking for next key", e);
+            return;
+        }
+
+        if (!maybeNextKey.isPresent()) {
+            Slog.d(TAG, "No secondary key rotation task pending. Exiting.");
+            return;
+        }
+
+        RecoverableKeyStoreSecondaryKey nextKey = maybeNextKey.get();
+        boolean isReady;
+        try {
+            isReady = isSecondaryKeyRotationReady(nextKey);
+        } catch (InternalRecoveryServiceException e) {
+            Slog.e(TAG, "Error encountered checking whether next secondary key is synced", e);
+            return;
+        }
+
+        if (!isReady) {
+            return;
+        }
+
+        try {
+            rotateToKey(nextKey);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error trying to rotate to new secondary key", e);
+        }
+    }
+
+    private Optional<RecoverableKeyStoreSecondaryKey> getNextKey()
+            throws InternalRecoveryServiceException, UnrecoverableKeyException {
+        Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
+        if (!maybeNextAlias.isPresent()) {
+            return Optional.empty();
+        }
+        return mSecondaryKeyManager.get(maybeNextAlias.get());
+    }
+
+    private boolean isSecondaryKeyRotationReady(RecoverableKeyStoreSecondaryKey nextKey)
+            throws InternalRecoveryServiceException {
+        String nextAlias = nextKey.getAlias();
+        Slog.i(TAG, "Key rotation to " + nextAlias + " is pending. Checking key sync status.");
+        int status = mRecoveryController.getRecoveryStatus(nextAlias);
+
+        if (status == RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE) {
+            Slog.e(
+                    TAG,
+                    "Permanent failure to sync " + nextAlias + ". Cannot possibly rotate to it.");
+            mCryptoSettings.removeNextSecondaryKeyAlias();
+            return false;
+        }
+
+        if (status == RecoveryController.RECOVERY_STATUS_SYNCED) {
+            Slog.i(TAG, "Secondary key " + nextAlias + " has now synced! Commencing rotation.");
+        } else {
+            Slog.i(TAG, "Sync still pending for " + nextAlias);
+        }
+        return status == RecoveryController.RECOVERY_STATUS_SYNCED;
+    }
+
+    /**
+     * @throws ActiveSecondaryNotInKeychainException if the currently active secondary key is not in
+     *     the keychain.
+     * @throws IOException if there is an IO issue communicating with the server or loading from
+     *     disk.
+     * @throws NoActiveSecondaryKeyException if there is no active key set.
+     * @throws IllegalBlockSizeException if there is an issue decrypting a tertiary key.
+     * @throws InvalidKeyException if any of the secondary keys cannot be used for wrapping or
+     *     unwrapping tertiary keys.
+     */
+    private void rotateToKey(RecoverableKeyStoreSecondaryKey newSecondaryKey)
+            throws ActiveSecondaryNotInKeychainException, IOException,
+                    NoActiveSecondaryKeyException, IllegalBlockSizeException, InvalidKeyException,
+                    InternalRecoveryServiceException, UnrecoverableKeyException,
+                    InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+                    NoSuchPaddingException {
+        RecoverableKeyStoreSecondaryKey activeSecondaryKey = getActiveSecondaryKey();
+        String activeSecondaryKeyAlias = activeSecondaryKey.getAlias();
+        String newSecondaryKeyAlias = newSecondaryKey.getAlias();
+        if (newSecondaryKeyAlias.equals(activeSecondaryKeyAlias)) {
+            Slog.i(TAG, activeSecondaryKeyAlias + " was already the active alias.");
+            return;
+        }
+
+        TertiaryKeyStore tertiaryKeyStore =
+                TertiaryKeyStore.newInstance(mContext, activeSecondaryKey);
+        Map<String, SecretKey> tertiaryKeys = tertiaryKeyStore.getAll();
+
+        if (tertiaryKeys.isEmpty()) {
+            Slog.i(
+                    TAG,
+                    "No tertiary keys for " + activeSecondaryKeyAlias + ". No need to rewrap. ");
+            mBackupServer.setActiveSecondaryKeyAlias(
+                    newSecondaryKeyAlias, /*tertiaryKeys=*/ Collections.emptyMap());
+        } else {
+            Map<String, WrappedKeyProto.WrappedKey> rewrappedTertiaryKeys =
+                    rewrapAll(newSecondaryKey, tertiaryKeys);
+            TertiaryKeyStore.newInstance(mContext, newSecondaryKey).putAll(rewrappedTertiaryKeys);
+            Slog.i(
+                    TAG,
+                    "Successfully rewrapped " + rewrappedTertiaryKeys.size() + " tertiary keys");
+            mBackupServer.setActiveSecondaryKeyAlias(newSecondaryKeyAlias, rewrappedTertiaryKeys);
+            Slog.i(
+                    TAG,
+                    "Successfully uploaded new set of tertiary keys to "
+                            + newSecondaryKeyAlias
+                            + " alias");
+        }
+
+        mCryptoSettings.setActiveSecondaryKeyAlias(newSecondaryKeyAlias);
+        mCryptoSettings.removeNextSecondaryKeyAlias();
+        try {
+            mRecoveryController.removeKey(activeSecondaryKeyAlias);
+        } catch (InternalRecoveryServiceException e) {
+            Slog.e(TAG, "Error removing old secondary key from RecoverableKeyStoreLoader", e);
+        }
+    }
+
+    private RecoverableKeyStoreSecondaryKey getActiveSecondaryKey()
+            throws NoActiveSecondaryKeyException, ActiveSecondaryNotInKeychainException,
+                    InternalRecoveryServiceException, UnrecoverableKeyException {
+
+        Optional<String> activeSecondaryAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
+
+        if (!activeSecondaryAlias.isPresent()) {
+            Slog.i(
+                    TAG,
+                    "Was asked to rotate secondary key, but local config did not have a secondary "
+                            + "key alias set.");
+            throw new NoActiveSecondaryKeyException("No local active secondary key set.");
+        }
+
+        String activeSecondaryKeyAlias = activeSecondaryAlias.get();
+        Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
+                mSecondaryKeyManager.get(activeSecondaryKeyAlias);
+
+        if (!secondaryKey.isPresent()) {
+            throw new ActiveSecondaryNotInKeychainException(
+                    String.format(
+                            Locale.US,
+                            "Had local active recoverable key alias of %s but key was not in"
+                                + " user's keychain.",
+                            activeSecondaryKeyAlias));
+        }
+
+        return secondaryKey.get();
+    }
+
+    /**
+     * Rewraps all the tertiary keys.
+     *
+     * @param newSecondaryKey The secondary key with which to rewrap the tertiaries.
+     * @param tertiaryKeys The tertiary keys, by package name.
+     * @return The newly wrapped tertiary keys, by package name.
+     * @throws InvalidKeyException if any key is unusable.
+     * @throws IllegalBlockSizeException if could not decrypt.
+     */
+    private Map<String, WrappedKeyProto.WrappedKey> rewrapAll(
+            RecoverableKeyStoreSecondaryKey newSecondaryKey, Map<String, SecretKey> tertiaryKeys)
+            throws InvalidKeyException, IllegalBlockSizeException, NoSuchPaddingException,
+                    NoSuchAlgorithmException {
+        Map<String, WrappedKeyProto.WrappedKey> wrappedKeys = new HashMap<>();
+
+        for (String packageName : tertiaryKeys.keySet()) {
+            SecretKey tertiaryKey = tertiaryKeys.get(packageName);
+            wrappedKeys.put(
+                    packageName, KeyWrapUtils.wrap(newSecondaryKey.getSecretKey(), tertiaryKey));
+        }
+
+        return wrappedKeys;
+    }
+}
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
similarity index 61%
copy from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
copy to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
index 10d0551..515db86 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,13 +12,13 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package com.android.frameworks.coretests;
+package com.android.server.backup.encryption.tasks;
 
-import android.app.Activity;
-
-public class TestActivity extends Activity {
-
+/** Exception thrown when aa backup has exceeded the space allowed for that user */
+public class SizeQuotaExceededException extends RuntimeException {
+    public SizeQuotaExceededException() {
+        super("Backup size quota exceeded.");
+    }
 }
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
new file mode 100644
index 0000000..9a97e38
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Thrown when the backup file provided by the server uses encryption algorithms this version of
+ * backup does not support. This could happen if the backup was created with a newer version of the
+ * code.
+ */
+public class UnsupportedEncryptedFileException extends EncryptedRestoreException {
+    public UnsupportedEncryptedFileException(String message) {
+        super(message);
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric-integration/Android.bp b/packages/BackupEncryption/test/robolectric-integration/Android.bp
new file mode 100644
index 0000000..f696278
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_robolectric_test {
+    name: "BackupEncryptionRoboIntegTests",
+    srcs: [
+        "src/**/*.java",
+    ],
+    java_resource_dirs: ["config"],
+    libs: [
+        "backup-encryption-protos",
+        "platform-test-annotations",
+        "testng",
+        "truth-prebuilt",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+    ],
+    instrumentation_for: "BackupEncryption",
+}
diff --git a/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml b/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
new file mode 100644
index 0000000..c3930cc
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          coreApp="true"
+          package="com.android.server.backup.encryption.robointeg">
+
+    <application/>
+
+</manifest>
diff --git a/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties b/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
new file mode 100644
index 0000000..26fceb3
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+sdk=NEWEST_SDK
diff --git a/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java b/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
new file mode 100644
index 0000000..8ec68fd
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.encryption.tasks.EncryptedFullBackupTask;
+import com.android.server.backup.encryption.tasks.EncryptedFullRestoreTask;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Map;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+@RunWith(RobolectricTestRunner.class)
+public class RoundTripTest {
+    /** Amount of data we want to round trip in this test */
+    private static final int TEST_DATA_SIZE = 1024 * 1024; // 1MB
+
+    /** Buffer size used when reading data from the restore task */
+    private static final int READ_BUFFER_SIZE = 1024; // 1024 byte buffer.
+
+    /** Key parameters used for the secondary encryption key */
+    private static final String KEY_ALGORITHM = "AES";
+    private static final int KEY_SIZE_BITS = 256;
+
+    /** Package name for our test package */
+    private static final String TEST_PACKAGE_NAME = "com.android.backup.test";
+
+    /** The name we use to refer to our secondary key */
+    private static final String TEST_KEY_ALIAS = "test/backup/KEY_ALIAS";
+
+    /** Original data used for comparison after round trip */
+    private final byte[] mOriginalData = new byte[TEST_DATA_SIZE];
+
+    /** App context, used to store the key data and chunk listings */
+    private Context mContext;
+
+    /** The secondary key we're using for the test */
+    private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+
+    /** Source of random material which is considered non-predictable in its' generation */
+    private SecureRandom mSecureRandom = new SecureRandom();
+
+    @Before
+    public void setUp() throws NoSuchAlgorithmException {
+        mContext = ApplicationProvider.getApplicationContext();
+        mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_KEY_ALIAS, generateAesKey());
+        fillBuffer(mOriginalData);
+    }
+
+    @Test
+    public void testRoundTrip() throws Exception {
+        byte[] backupData = performBackup(mOriginalData);
+        assertThat(backupData).isNotEqualTo(mOriginalData);
+        byte[] restoredData = performRestore(backupData);
+        assertThat(restoredData).isEqualTo(mOriginalData);
+    }
+
+    /** Perform a backup and return the backed-up representation of the data */
+    private byte[] performBackup(byte[] backupData) throws Exception {
+        DummyServer dummyServer = new DummyServer();
+        EncryptedFullBackupTask backupTask =
+                EncryptedFullBackupTask.newInstance(
+                        mContext,
+                        dummyServer,
+                        mSecureRandom,
+                        mSecondaryKey,
+                        TEST_PACKAGE_NAME,
+                        new ByteArrayInputStream(backupData));
+        backupTask.call();
+        return dummyServer.mStoredData;
+    }
+
+    /** Perform a restore and resturn the bytes obtained from the restore process */
+    private byte[] performRestore(byte[] backupData)
+            throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
+                    InvalidAlgorithmParameterException, InvalidKeyException,
+                    IllegalBlockSizeException {
+        ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
+
+        EncryptedFullRestoreTask restoreTask =
+                EncryptedFullRestoreTask.newInstance(
+                        mContext, new FakeFullRestoreDownloader(backupData), getTertiaryKey());
+
+        byte[] buffer = new byte[READ_BUFFER_SIZE];
+        int bytesRead = restoreTask.readNextChunk(buffer);
+        while (bytesRead != -1) {
+            decryptedOutput.write(buffer, 0, bytesRead);
+            bytesRead = restoreTask.readNextChunk(buffer);
+        }
+
+        return decryptedOutput.toByteArray();
+    }
+
+    /** Get the tertiary key for our test package from the key manager */
+    private SecretKey getTertiaryKey()
+            throws IllegalBlockSizeException, InvalidAlgorithmParameterException,
+                    NoSuchAlgorithmException, IOException, NoSuchPaddingException,
+                    InvalidKeyException {
+        TertiaryKeyManager tertiaryKeyManager =
+                new TertiaryKeyManager(
+                        mContext,
+                        mSecureRandom,
+                        TertiaryKeyRotationScheduler.getInstance(mContext),
+                        mSecondaryKey,
+                        TEST_PACKAGE_NAME);
+        return tertiaryKeyManager.getKey();
+    }
+
+    /** Fill a buffer with data in a predictable way */
+    private void fillBuffer(byte[] buffer) {
+        byte loopingCounter = 0;
+        for (int i = 0; i < buffer.length; i++) {
+            buffer[i] = loopingCounter;
+            loopingCounter++;
+        }
+    }
+
+    /** Generate a new, random, AES key */
+    public static SecretKey generateAesKey() throws NoSuchAlgorithmException {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+        keyGenerator.init(KEY_SIZE_BITS);
+        return keyGenerator.generateKey();
+    }
+
+    /**
+     * Dummy backup data endpoint. This stores the data so we can use it
+     * in subsequent test steps.
+     */
+    private static class DummyServer implements CryptoBackupServer {
+        private static final String DUMMY_DOC_ID = "DummyDoc";
+
+        byte[] mStoredData = null;
+
+        @Override
+        public String uploadIncrementalBackup(
+                String packageName,
+                String oldDocId,
+                byte[] diffScript,
+                WrappedKeyProto.WrappedKey tertiaryKey) {
+            throw new RuntimeException("Not Implemented");
+        }
+
+        @Override
+        public String uploadNonIncrementalBackup(
+                String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
+            assertThat(packageName).isEqualTo(TEST_PACKAGE_NAME);
+            mStoredData = data;
+            return DUMMY_DOC_ID;
+        }
+
+        @Override
+        public void setActiveSecondaryKeyAlias(
+                String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
+            throw new RuntimeException("Not Implemented");
+        }
+    }
+
+    /** Fake package wrapper which returns data from a byte array. */
+    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+        private final ByteArrayInputStream mData;
+
+        FakeFullRestoreDownloader(byte[] data) {
+            // We override all methods of the superclass, so it does not require any collaborators.
+            super();
+            mData = new ByteArrayInputStream(data);
+        }
+
+        @Override
+        public int readNextChunk(byte[] buffer) throws IOException {
+            return mData.read(buffer);
+        }
+
+        @Override
+        public void finish(FinishType finishType) {
+            // Do nothing.
+        }
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
new file mode 100644
index 0000000..07a6fd2
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+import static com.android.server.backup.testing.CryptoTestUtils.newChunkOrdering;
+import static com.android.server.backup.testing.CryptoTestUtils.newChunksMetadata;
+import static com.android.server.backup.testing.CryptoTestUtils.newPair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupDataInput;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.kv.DecryptedChunkKvOutput;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto.KeyValuePair;
+import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.nano.MessageNano;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.RandomAccessFile;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@Config(shadows = {ShadowBackupDataInput.class})
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BackupFileDecryptorTaskTest {
+    private static final String READ_WRITE_MODE = "rw";
+    private static final int BYTES_PER_KILOBYTE = 1024;
+    private static final int MIN_CHUNK_SIZE_BYTES = 2 * BYTES_PER_KILOBYTE;
+    private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * BYTES_PER_KILOBYTE;
+    private static final int MAX_CHUNK_SIZE_BYTES = 64 * BYTES_PER_KILOBYTE;
+    private static final int BACKUP_DATA_SIZE_BYTES = 60 * BYTES_PER_KILOBYTE;
+    private static final int GCM_NONCE_LENGTH_BYTES = 12;
+    private static final int GCM_TAG_LENGTH_BYTES = 16;
+    private static final int BITS_PER_BYTE = 8;
+    private static final int CHECKSUM_LENGTH_BYTES = 256 / BITS_PER_BYTE;
+    @Nullable private static final FileDescriptor NULL_FILE_DESCRIPTOR = null;
+
+    private static final Set<KeyValuePair> TEST_KV_DATA = new HashSet<>();
+
+    static {
+        TEST_KV_DATA.add(newPair("key1", "value1"));
+        TEST_KV_DATA.add(newPair("key2", "value2"));
+    }
+
+    @Rule public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    private SecretKey mTertiaryKey;
+    private SecretKey mChunkEncryptionKey;
+    private File mInputFile;
+    private File mOutputFile;
+    private DecryptedChunkOutput mFileOutput;
+    private DecryptedChunkKvOutput mKvOutput;
+    private Random mRandom;
+    private BackupFileDecryptorTask mTask;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mRandom = new Random();
+        mTertiaryKey = generateAesKey();
+        // In good situations it's always the same. We allow changing it for testing when somehow it
+        // has become mismatched that we throw an error.
+        mChunkEncryptionKey = mTertiaryKey;
+        mInputFile = mTemporaryFolder.newFile();
+        mOutputFile = mTemporaryFolder.newFile();
+        mFileOutput = new DecryptedChunkFileOutput(mOutputFile);
+        mKvOutput = new DecryptedChunkKvOutput(new ChunkHasher(mTertiaryKey));
+        mTask = new BackupFileDecryptorTask(mTertiaryKey);
+    }
+
+    @Test
+    public void decryptFile_throwsForNonExistentInput() throws Exception {
+        assertThrows(
+                FileNotFoundException.class,
+                () ->
+                        mTask.decryptFile(
+                                new File(mTemporaryFolder.newFolder(), "nonexistent"),
+                                mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForDirectoryInputFile() throws Exception {
+        assertThrows(
+                FileNotFoundException.class,
+                () -> mTask.decryptFile(mTemporaryFolder.newFolder(), mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_withExplicitStarts_decryptsEncryptedData() throws Exception {
+        byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
+        createEncryptedFileUsingExplicitStarts(backupData);
+
+        mTask.decryptFile(mInputFile, mFileOutput);
+
+        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+    }
+
+    @Test
+    public void decryptFile_withInlineLengths_decryptsEncryptedData() throws Exception {
+        createEncryptedFileUsingInlineLengths(
+                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+        mTask.decryptFile(mInputFile, mKvOutput);
+        assertThat(asMap(mKvOutput.getPairs())).containsExactlyEntriesIn(asMap(TEST_KV_DATA));
+    }
+
+    @Test
+    public void decryptFile_withNoChunkOrderingType_decryptsUsingExplicitStarts() throws Exception {
+        byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
+        createEncryptedFileUsingExplicitStarts(
+                backupData,
+                chunkOrdering -> chunkOrdering,
+                chunksMetadata -> {
+                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+                    metadata.chunkOrderingType =
+                            ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+                    return metadata;
+                });
+
+        mTask.decryptFile(mInputFile, mFileOutput);
+
+        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+    }
+
+    @Test
+    public void decryptFile_withInlineLengths_throwsForZeroLengths() throws Exception {
+        createEncryptedFileUsingInlineLengths(
+                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+
+        // Set the length of the first chunk to zero.
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(0);
+        raf.writeInt(0);
+
+        assertThrows(
+                MalformedEncryptedFileException.class,
+                () -> mTask.decryptFile(mInputFile, mKvOutput));
+    }
+
+    @Test
+    public void decryptFile_withInlineLengths_throwsForLongLengths() throws Exception {
+        createEncryptedFileUsingInlineLengths(
+                TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+
+        // Set the length of the first chunk to zero.
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(0);
+        raf.writeInt((int) mInputFile.length());
+
+        assertThrows(
+                MalformedEncryptedFileException.class,
+                () -> mTask.decryptFile(mInputFile, mKvOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForBadKey() throws Exception {
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        assertThrows(
+                AEADBadTagException.class,
+                () ->
+                        new BackupFileDecryptorTask(generateAesKey())
+                                .decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_withExplicitStarts_throwsForMangledOrdering() throws Exception {
+        createEncryptedFileUsingExplicitStarts(
+                randomData(BACKUP_DATA_SIZE_BYTES),
+                chunkOrdering -> {
+                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+                    Arrays.sort(ordering.starts);
+                    return ordering;
+                });
+
+        assertThrows(
+                MessageDigestMismatchException.class,
+                () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_withExplicitStarts_noChunks_returnsNoData() throws Exception {
+        byte[] backupData = randomData(/*length=*/ 0);
+        createEncryptedFileUsingExplicitStarts(
+                backupData,
+                chunkOrdering -> {
+                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+                    ordering.starts = new int[0];
+                    return ordering;
+                });
+
+        mTask.decryptFile(mInputFile, mFileOutput);
+
+        assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+    }
+
+    @Test
+    public void decryptFile_throwsForMismatchedChecksum() throws Exception {
+        createEncryptedFileUsingExplicitStarts(
+                randomData(BACKUP_DATA_SIZE_BYTES),
+                chunkOrdering -> {
+                    ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+                    ordering.checksum =
+                            Arrays.copyOf(randomData(CHECKSUM_LENGTH_BYTES), CHECKSUM_LENGTH_BYTES);
+                    return ordering;
+                });
+
+        assertThrows(
+                MessageDigestMismatchException.class,
+                () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForBadChunksMetadataOffset() throws Exception {
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        // Replace the metadata with all 1s.
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(raf.length() - Long.BYTES);
+        int metadataOffset = (int) raf.readLong();
+        int metadataLength = (int) raf.length() - metadataOffset - Long.BYTES;
+
+        byte[] allOnes = new byte[metadataLength];
+        Arrays.fill(allOnes, (byte) 1);
+
+        raf.seek(metadataOffset);
+        raf.write(allOnes, /*off=*/ 0, metadataLength);
+
+        MalformedEncryptedFileException thrown =
+                expectThrows(
+                        MalformedEncryptedFileException.class,
+                        () -> mTask.decryptFile(mInputFile, mFileOutput));
+        assertThat(thrown)
+                .hasMessageThat()
+                .isEqualTo(
+                        "Could not read chunks metadata at position "
+                                + metadataOffset
+                                + " of file of "
+                                + raf.length()
+                                + " bytes");
+    }
+
+    @Test
+    public void decryptFile_throwsForChunksMetadataOffsetBeyondEndOfFile() throws Exception {
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(raf.length() - Long.BYTES);
+        raf.writeLong(raf.length());
+
+        MalformedEncryptedFileException thrown =
+                expectThrows(
+                        MalformedEncryptedFileException.class,
+                        () -> mTask.decryptFile(mInputFile, mFileOutput));
+        assertThat(thrown)
+                .hasMessageThat()
+                .isEqualTo(
+                        raf.length()
+                                + " is not valid position for chunks metadata in file of "
+                                + raf.length()
+                                + " bytes");
+    }
+
+    @Test
+    public void decryptFile_throwsForChunksMetadataOffsetBeforeBeginningOfFile() throws Exception {
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(raf.length() - Long.BYTES);
+        raf.writeLong(-1);
+
+        MalformedEncryptedFileException thrown =
+                expectThrows(
+                        MalformedEncryptedFileException.class,
+                        () -> mTask.decryptFile(mInputFile, mFileOutput));
+        assertThat(thrown)
+                .hasMessageThat()
+                .isEqualTo(
+                        "-1 is not valid position for chunks metadata in file of "
+                                + raf.length()
+                                + " bytes");
+    }
+
+    @Test
+    public void decryptFile_throwsForMangledChunks() throws Exception {
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        // Mess up some bits in a random byte
+        RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+        raf.seek(50);
+        byte fiftiethByte = raf.readByte();
+        raf.seek(50);
+        raf.write(~fiftiethByte);
+
+        assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForBadChunkEncryptionKey() throws Exception {
+        mChunkEncryptionKey = generateAesKey();
+
+        createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+        assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForUnsupportedCipherType() throws Exception {
+        createEncryptedFileUsingExplicitStarts(
+                randomData(BACKUP_DATA_SIZE_BYTES),
+                chunkOrdering -> chunkOrdering,
+                chunksMetadata -> {
+                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+                    metadata.cipherType = ChunksMetadataProto.UNKNOWN_CIPHER_TYPE;
+                    return metadata;
+                });
+
+        assertThrows(
+                UnsupportedEncryptedFileException.class,
+                () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    @Test
+    public void decryptFile_throwsForUnsupportedMessageDigestType() throws Exception {
+        createEncryptedFileUsingExplicitStarts(
+                randomData(BACKUP_DATA_SIZE_BYTES),
+                chunkOrdering -> chunkOrdering,
+                chunksMetadata -> {
+                    ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+                    metadata.checksumType = ChunksMetadataProto.UNKNOWN_CHECKSUM_TYPE;
+                    return metadata;
+                });
+
+        assertThrows(
+                UnsupportedEncryptedFileException.class,
+                () -> mTask.decryptFile(mInputFile, mFileOutput));
+    }
+
+    /**
+     * Creates an encrypted backup file from the given data.
+     *
+     * @param data The plaintext content.
+     */
+    private void createEncryptedFileUsingExplicitStarts(byte[] data) throws Exception {
+        createEncryptedFileUsingExplicitStarts(data, chunkOrdering -> chunkOrdering);
+    }
+
+    /**
+     * Creates an encrypted backup file from the given data.
+     *
+     * @param data The plaintext content.
+     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+     */
+    private void createEncryptedFileUsingExplicitStarts(
+            byte[] data, Transformer<ChunkOrdering> chunkOrderingTransformer) throws Exception {
+        createEncryptedFileUsingExplicitStarts(
+                data, chunkOrderingTransformer, chunksMetadata -> chunksMetadata);
+    }
+
+    /**
+     * Creates an encrypted backup file from the given data in mode {@link
+     * ChunksMetadataProto#EXPLICIT_STARTS}.
+     *
+     * @param data The plaintext content.
+     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+     * @param chunksMetadataTransformer Transforms the metadata before it's written.
+     */
+    private void createEncryptedFileUsingExplicitStarts(
+            byte[] data,
+            Transformer<ChunkOrdering> chunkOrderingTransformer,
+            Transformer<ChunksMetadata> chunksMetadataTransformer)
+            throws Exception {
+        Result result = backupFullData(data);
+
+        ArrayList<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
+        Collections.shuffle(chunks);
+        HashMap<ChunkHash, Integer> startPositions = new HashMap<>();
+
+        try (FileOutputStream fos = new FileOutputStream(mInputFile);
+                DataOutputStream dos = new DataOutputStream(fos)) {
+            int position = 0;
+
+            for (EncryptedChunk chunk : chunks) {
+                startPositions.put(chunk.key(), position);
+                dos.write(chunk.nonce());
+                dos.write(chunk.encryptedBytes());
+                position += chunk.nonce().length + chunk.encryptedBytes().length;
+            }
+
+            int[] starts = new int[chunks.size()];
+            List<ChunkHash> chunkListing = result.getAllChunks();
+
+            for (int i = 0; i < chunks.size(); i++) {
+                starts[i] = startPositions.get(chunkListing.get(i));
+            }
+
+            ChunkOrdering chunkOrdering = newChunkOrdering(starts, result.getDigest());
+            chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
+
+            ChunksMetadata metadata =
+                    newChunksMetadata(
+                            ChunksMetadataProto.AES_256_GCM,
+                            ChunksMetadataProto.SHA_256,
+                            ChunksMetadataProto.EXPLICIT_STARTS,
+                            encrypt(chunkOrdering));
+            metadata = chunksMetadataTransformer.accept(metadata);
+
+            dos.write(MessageNano.toByteArray(metadata));
+            dos.writeLong(position);
+        }
+    }
+
+    /**
+     * Creates an encrypted backup file from the given data in mode {@link
+     * ChunksMetadataProto#INLINE_LENGTHS}.
+     *
+     * @param data The plaintext key value pairs to back up.
+     * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+     * @param chunksMetadataTransformer Transforms the metadata before it's written.
+     */
+    private void createEncryptedFileUsingInlineLengths(
+            Set<KeyValuePair> data,
+            Transformer<ChunkOrdering> chunkOrderingTransformer,
+            Transformer<ChunksMetadata> chunksMetadataTransformer)
+            throws Exception {
+        Result result = backupKvData(data);
+
+        List<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
+        System.out.println("we have chunk count " + chunks.size());
+        Collections.shuffle(chunks);
+
+        try (FileOutputStream fos = new FileOutputStream(mInputFile);
+                DataOutputStream dos = new DataOutputStream(fos)) {
+            for (EncryptedChunk chunk : chunks) {
+                dos.writeInt(chunk.nonce().length + chunk.encryptedBytes().length);
+                dos.write(chunk.nonce());
+                dos.write(chunk.encryptedBytes());
+            }
+
+            ChunkOrdering chunkOrdering = newChunkOrdering(null, result.getDigest());
+            chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
+
+            ChunksMetadata metadata =
+                    newChunksMetadata(
+                            ChunksMetadataProto.AES_256_GCM,
+                            ChunksMetadataProto.SHA_256,
+                            ChunksMetadataProto.INLINE_LENGTHS,
+                            encrypt(chunkOrdering));
+            metadata = chunksMetadataTransformer.accept(metadata);
+
+            int metadataStart = dos.size();
+            dos.write(MessageNano.toByteArray(metadata));
+            dos.writeLong(metadataStart);
+        }
+    }
+
+    /** Performs a full backup of the given data, and returns the chunks. */
+    private BackupEncrypter.Result backupFullData(byte[] data) throws Exception {
+        BackupStreamEncrypter encrypter =
+                new BackupStreamEncrypter(
+                        new ByteArrayInputStream(data),
+                        MIN_CHUNK_SIZE_BYTES,
+                        MAX_CHUNK_SIZE_BYTES,
+                        AVERAGE_CHUNK_SIZE_BYTES);
+        return encrypter.backup(
+                mChunkEncryptionKey,
+                randomData(FingerprintMixer.SALT_LENGTH_BYTES),
+                new HashSet<>());
+    }
+
+    private Result backupKvData(Set<KeyValuePair> data) throws Exception {
+        ShadowBackupDataInput.reset();
+        for (KeyValuePair pair : data) {
+            ShadowBackupDataInput.addEntity(pair.key, pair.value);
+        }
+        KvBackupEncrypter encrypter =
+                new KvBackupEncrypter(new BackupDataInput(NULL_FILE_DESCRIPTOR));
+        return encrypter.backup(
+                mChunkEncryptionKey,
+                randomData(FingerprintMixer.SALT_LENGTH_BYTES),
+                Collections.EMPTY_SET);
+    }
+
+    /** Encrypts {@code chunkOrdering} using {@link #mTertiaryKey}. */
+    private byte[] encrypt(ChunkOrdering chunkOrdering) throws Exception {
+        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+        byte[] nonce = randomData(GCM_NONCE_LENGTH_BYTES);
+        cipher.init(
+                Cipher.ENCRYPT_MODE,
+                mTertiaryKey,
+                new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE, nonce));
+        byte[] nanoBytes = MessageNano.toByteArray(chunkOrdering);
+        byte[] encryptedBytes = cipher.doFinal(nanoBytes);
+
+        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            out.write(nonce);
+            out.write(encryptedBytes);
+            return out.toByteArray();
+        }
+    }
+
+    /** Returns {@code length} random bytes. */
+    private byte[] randomData(int length) {
+        byte[] data = new byte[length];
+        mRandom.nextBytes(data);
+        return data;
+    }
+
+    private static ImmutableMap<String, String> asMap(Collection<KeyValuePair> pairs) {
+        ImmutableMap.Builder<String, String> map = ImmutableMap.builder();
+        for (KeyValuePair pair : pairs) {
+            map.put(pair.key, new String(pair.value, Charset.forName("UTF-8")));
+        }
+        return map.build();
+    }
+
+    private interface Transformer<T> {
+        T accept(T t);
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
new file mode 100644
index 0000000..096b2da
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto.WrappedKey;
+import com.android.server.backup.testing.CryptoTestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+@Config(shadows = {EncryptedBackupTaskTest.ShadowBackupFileBuilder.class})
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedFullBackupTaskTest {
+    private static final String TEST_PACKAGE_NAME = "com.example.package";
+    private static final byte[] TEST_EXISTING_FINGERPRINT_MIXER_SALT =
+            Arrays.copyOf(new byte[] {11}, ChunkHash.HASH_LENGTH_BYTES);
+    private static final byte[] TEST_GENERATED_FINGERPRINT_MIXER_SALT =
+            Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
+    private static final ChunkHash TEST_CHUNK_HASH_1 =
+            new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
+    private static final ChunkHash TEST_CHUNK_HASH_2 =
+            new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
+    private static final int TEST_CHUNK_LENGTH_1 = 20;
+    private static final int TEST_CHUNK_LENGTH_2 = 40;
+
+    @Mock private ProtoStore<ChunkListing> mChunkListingStore;
+    @Mock private TertiaryKeyManager mTertiaryKeyManager;
+    @Mock private InputStream mInputStream;
+    @Mock private EncryptedBackupTask mEncryptedBackupTask;
+    @Mock private SecretKey mTertiaryKey;
+    @Mock private SecureRandom mSecureRandom;
+
+    private EncryptedFullBackupTask mTask;
+    private ChunkListing mOldChunkListing;
+    private ChunkListing mNewChunkListing;
+    private WrappedKey mWrappedTertiaryKey;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mWrappedTertiaryKey = new WrappedKey();
+        when(mTertiaryKeyManager.getKey()).thenReturn(mTertiaryKey);
+        when(mTertiaryKeyManager.getWrappedKey()).thenReturn(mWrappedTertiaryKey);
+
+        mOldChunkListing =
+                CryptoTestUtils.newChunkListing(
+                        /* docId */ null,
+                        TEST_EXISTING_FINGERPRINT_MIXER_SALT,
+                        ChunksMetadataProto.AES_256_GCM,
+                        ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
+                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1));
+        mNewChunkListing =
+                CryptoTestUtils.newChunkListing(
+                        /* docId */ null,
+                        /* fingerprintSalt */ null,
+                        ChunksMetadataProto.AES_256_GCM,
+                        ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
+                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1),
+                        CryptoTestUtils.newChunk(TEST_CHUNK_HASH_2.getHash(), TEST_CHUNK_LENGTH_2));
+        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+                .thenReturn(mNewChunkListing);
+        when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
+                .thenReturn(mNewChunkListing);
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+
+        doAnswer(invocation -> {
+            byte[] byteArray = (byte[]) invocation.getArguments()[0];
+            System.arraycopy(
+                    TEST_GENERATED_FINGERPRINT_MIXER_SALT,
+                    /* srcPos */ 0,
+                    byteArray,
+                    /* destPos */ 0,
+                    FingerprintMixer.SALT_LENGTH_BYTES);
+            return null;
+        })
+                .when(mSecureRandom)
+                .nextBytes(any(byte[].class));
+
+        mTask =
+                new EncryptedFullBackupTask(
+                        mChunkListingStore,
+                        mTertiaryKeyManager,
+                        mEncryptedBackupTask,
+                        mInputStream,
+                        TEST_PACKAGE_NAME,
+                        mSecureRandom);
+    }
+
+    @Test
+    public void call_existingChunkListingButTertiaryKeyRotated_performsNonIncrementalBackup()
+            throws Exception {
+        when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+                .thenReturn(Optional.of(mOldChunkListing));
+
+        mTask.call();
+
+        verify(mEncryptedBackupTask)
+                .performNonIncrementalBackup(
+                        eq(mTertiaryKey),
+                        eq(mWrappedTertiaryKey),
+                        eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
+    }
+
+    @Test
+    public void call_noExistingChunkListing_performsNonIncrementalBackup() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+        mTask.call();
+        verify(mEncryptedBackupTask)
+                .performNonIncrementalBackup(
+                        eq(mTertiaryKey),
+                        eq(mWrappedTertiaryKey),
+                        eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
+    }
+
+    @Test
+    public void call_existingChunkListing_performsIncrementalBackup() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+                .thenReturn(Optional.of(mOldChunkListing));
+        mTask.call();
+        verify(mEncryptedBackupTask)
+                .performIncrementalBackup(
+                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
+    }
+
+    @Test
+    public void
+            call_existingChunkListingWithNoFingerprintMixerSalt_doesntSetSaltBeforeIncBackup()
+                    throws Exception {
+        mOldChunkListing.fingerprintMixerSalt = new byte[0];
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+                .thenReturn(Optional.of(mOldChunkListing));
+
+        mTask.call();
+
+        verify(mEncryptedBackupTask)
+                .performIncrementalBackup(
+                        eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
+    }
+
+    @Test
+    public void call_noExistingChunkListing_storesNewChunkListing() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+        mTask.call();
+        verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
+    }
+
+    @Test
+    public void call_existingChunkListing_storesNewChunkListing() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+                .thenReturn(Optional.of(mOldChunkListing));
+        mTask.call();
+        verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
+    }
+
+    @Test
+    public void call_exceptionDuringBackup_doesNotSaveNewChunkListing() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+        when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+                .thenThrow(GeneralSecurityException.class);
+
+        assertThrows(Exception.class, () -> mTask.call());
+
+        assertThat(mChunkListingStore.loadProto(TEST_PACKAGE_NAME).isPresent()).isFalse();
+    }
+
+    @Test
+    public void call_incrementalThrowsPermanentException_clearsState() throws Exception {
+        when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+                .thenReturn(Optional.of(mOldChunkListing));
+        when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
+                .thenThrow(IOException.class);
+
+        assertThrows(IOException.class, () -> mTask.call());
+
+        verify(mChunkListingStore).deleteProto(TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void call_closesInputStream() throws Exception {
+        mTask.call();
+        verify(mInputStream).close();
+    }
+
+    @Test
+    public void cancel_cancelsTask() throws Exception {
+        mTask.cancel();
+        verify(mEncryptedBackupTask).cancel();
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
new file mode 100644
index 0000000..0affacd
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+import static java.util.stream.Collectors.toList;
+
+import com.android.server.backup.encryption.FullRestoreDownloader;
+
+import com.google.common.io.Files;
+import com.google.common.primitives.Bytes;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedFullRestoreTaskTest {
+    private static final int TEST_BUFFER_SIZE = 10;
+    private static final byte[] TEST_ENCRYPTED_DATA = {1, 2, 3, 4, 5, 6};
+    private static final byte[] TEST_DECRYPTED_DATA = fakeDecrypt(TEST_ENCRYPTED_DATA);
+
+    @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+    @Mock private BackupFileDecryptorTask mDecryptorTask;
+
+    private File mFolder;
+    private FakeFullRestoreDownloader mFullRestorePackageWrapper;
+    private EncryptedFullRestoreTask mTask;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mFolder = temporaryFolder.newFolder();
+        mFullRestorePackageWrapper = new FakeFullRestoreDownloader(TEST_ENCRYPTED_DATA);
+
+        doAnswer(
+            invocation -> {
+                File source = invocation.getArgument(0);
+                DecryptedChunkOutput target = invocation.getArgument(1);
+                byte[] decrypted = fakeDecrypt(Files.toByteArray(source));
+                target.open();
+                target.processChunk(decrypted, decrypted.length);
+                target.close();
+                return null;
+            })
+                .when(mDecryptorTask)
+                .decryptFile(any(), any());
+
+        mTask = new EncryptedFullRestoreTask(mFolder, mFullRestorePackageWrapper, mDecryptorTask);
+    }
+
+    @Test
+    public void readNextChunk_downloadsAndDecryptsBackup() throws Exception {
+        ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
+
+        byte[] buffer = new byte[TEST_BUFFER_SIZE];
+        int bytesRead = mTask.readNextChunk(buffer);
+        while (bytesRead != -1) {
+            decryptedOutput.write(buffer, 0, bytesRead);
+            bytesRead = mTask.readNextChunk(buffer);
+        }
+
+        assertThat(decryptedOutput.toByteArray()).isEqualTo(TEST_DECRYPTED_DATA);
+    }
+
+    @Test
+    public void finish_deletesTemporaryFiles() throws Exception {
+        mTask.readNextChunk(new byte[10]);
+        mTask.finish(FullRestoreDownloader.FinishType.UNKNOWN_FINISH);
+
+        assertThat(mFolder.listFiles()).isEmpty();
+    }
+
+    /** Fake package wrapper which returns data from a byte array. */
+    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+        private final ByteArrayInputStream mData;
+
+        FakeFullRestoreDownloader(byte[] data) {
+            // We override all methods of the superclass, so it does not require any collaborators.
+            super();
+            mData = new ByteArrayInputStream(data);
+        }
+
+        @Override
+        public int readNextChunk(byte[] buffer) throws IOException {
+            return mData.read(buffer);
+        }
+
+        @Override
+        public void finish(FinishType finishType) {
+            // Nothing to do.
+        }
+    }
+
+    /** Fake decrypts a byte array by subtracting 1 from each byte. */
+    private static byte[] fakeDecrypt(byte[] input) {
+        return Bytes.toArray(Bytes.asList(input).stream().map(b -> b + 1).collect(toList()));
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
new file mode 100644
index 0000000..de8b734
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
+
+import com.google.common.io.Files;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Random;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class FullRestoreToFileTaskTest {
+    private static final int TEST_RANDOM_SEED = 34;
+    private static final int TEST_MAX_CHUNK_SIZE_BYTES = 5;
+    private static final int TEST_DATA_LENGTH_BYTES = TEST_MAX_CHUNK_SIZE_BYTES * 20;
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    private byte[] mTestData;
+    private File mTargetFile;
+    private FakeFullRestoreDownloader mFakeFullRestoreDownloader;
+    @Mock private FullRestoreDownloader mMockFullRestoreDownloader;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTargetFile = mTemporaryFolder.newFile();
+
+        mTestData = new byte[TEST_DATA_LENGTH_BYTES];
+        new Random(TEST_RANDOM_SEED).nextBytes(mTestData);
+        mFakeFullRestoreDownloader = new FakeFullRestoreDownloader(mTestData);
+    }
+
+    private FullRestoreToFileTask createTaskWithFakeDownloader() {
+        return new FullRestoreToFileTask(mFakeFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
+    }
+
+    private FullRestoreToFileTask createTaskWithMockDownloader() {
+        return new FullRestoreToFileTask(mMockFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
+    }
+
+    @Test
+    public void restoreToFile_readsDataAndWritesToFile() throws Exception {
+        FullRestoreToFileTask task = createTaskWithFakeDownloader();
+        task.restoreToFile(mTargetFile);
+        assertThat(Files.toByteArray(mTargetFile)).isEqualTo(mTestData);
+    }
+
+    @Test
+    public void restoreToFile_noErrors_closesDownloaderWithFinished() throws Exception {
+        FullRestoreToFileTask task = createTaskWithMockDownloader();
+        when(mMockFullRestoreDownloader.readNextChunk(any())).thenReturn(-1);
+
+        task.restoreToFile(mTargetFile);
+
+        verify(mMockFullRestoreDownloader).finish(FinishType.FINISHED);
+    }
+
+    @Test
+    public void restoreToFile_ioException_closesDownloaderWithTransferFailure() throws Exception {
+        FullRestoreToFileTask task = createTaskWithMockDownloader();
+        when(mMockFullRestoreDownloader.readNextChunk(any())).thenThrow(IOException.class);
+
+        assertThrows(IOException.class, () -> task.restoreToFile(mTargetFile));
+
+        verify(mMockFullRestoreDownloader).finish(FinishType.TRANSFER_FAILURE);
+    }
+
+    /** Fake package wrapper which returns data from a byte array. */
+    private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+
+        private final ByteArrayInputStream mData;
+
+        FakeFullRestoreDownloader(byte[] data) {
+            // We override all methods of the superclass, so it does not require any collaborators.
+            super();
+            this.mData = new ByteArrayInputStream(data);
+        }
+
+        @Override
+        public int readNextChunk(byte[] buffer) throws IOException {
+            return mData.read(buffer);
+        }
+
+        @Override
+        public void finish(FinishType finishType) {
+            // Do nothing.
+        }
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
new file mode 100644
index 0000000..cda7317
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertFalse;
+
+import android.app.Application;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyStore;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.testing.fakes.FakeCryptoBackupServer;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowRecoveryController.class})
+public class RotateSecondaryKeyTaskTest {
+    private static final String APP_1 = "app1";
+    private static final String APP_2 = "app2";
+    private static final String APP_3 = "app3";
+
+    private static final String CURRENT_SECONDARY_KEY_ALIAS =
+            "recoverablekey.alias/d524796bd07de3c2225c63d434eff698";
+    private static final String NEXT_SECONDARY_KEY_ALIAS =
+            "recoverablekey.alias/6c6d198a7f12e662b6bc45f4849db170";
+
+    private Application mApplication;
+    private RotateSecondaryKeyTask mTask;
+    private RecoveryController mRecoveryController;
+    private FakeCryptoBackupServer mBackupServer;
+    private CryptoSettings mCryptoSettings;
+    private Map<String, SecretKey> mTertiaryKeysByPackageName;
+    private RecoverableKeyStoreSecondaryKeyManager mRecoverableSecondaryKeyManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mApplication = ApplicationProvider.getApplicationContext();
+
+        mTertiaryKeysByPackageName = new HashMap<>();
+        mTertiaryKeysByPackageName.put(APP_1, generateAesKey());
+        mTertiaryKeysByPackageName.put(APP_2, generateAesKey());
+        mTertiaryKeysByPackageName.put(APP_3, generateAesKey());
+
+        mRecoveryController = RecoveryController.getInstance(mApplication);
+        mRecoverableSecondaryKeyManager =
+                new RecoverableKeyStoreSecondaryKeyManager(
+                        RecoveryController.getInstance(mApplication), new SecureRandom());
+        mBackupServer = new FakeCryptoBackupServer();
+        mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
+        addNextSecondaryKeyToRecoveryController();
+        mCryptoSettings.setNextSecondaryAlias(NEXT_SECONDARY_KEY_ALIAS);
+
+        mTask =
+                new RotateSecondaryKeyTask(
+                        mApplication,
+                        mRecoverableSecondaryKeyManager,
+                        mBackupServer,
+                        mCryptoSettings,
+                        mRecoveryController);
+
+        ShadowRecoveryController.reset();
+    }
+
+    @Test
+    public void run_failsIfThereIsNoActiveSecondaryKey() throws Exception {
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertFalse(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent());
+    }
+
+    @Test
+    public void run_failsIfActiveSecondaryIsNotInRecoveryController() throws Exception {
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        // Have to add it first as otherwise CryptoSettings throws an exception when trying to set
+        // it
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_doesNothingIfFlagIsDisabled() throws Exception {
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+        addWrappedTertiaries();
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_setsActiveSecondary() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+        addWrappedTertiaries();
+
+        mTask.run();
+
+        assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_rewrapsExistingTertiaryKeys() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+        addWrappedTertiaries();
+
+        mTask.run();
+
+        Map<String, WrappedKeyProto.WrappedKey> rewrappedKeys =
+                mBackupServer.getAllTertiaryKeys(NEXT_SECONDARY_KEY_ALIAS);
+        SecretKey secondaryKey = (SecretKey) mRecoveryController.getKey(NEXT_SECONDARY_KEY_ALIAS);
+        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+            WrappedKeyProto.WrappedKey rewrappedKey = rewrappedKeys.get(packageName);
+            assertThat(KeyWrapUtils.unwrap(secondaryKey, rewrappedKey))
+                    .isEqualTo(mTertiaryKeysByPackageName.get(packageName));
+        }
+    }
+
+    @Test
+    public void run_persistsRewrappedKeysToDisk() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+        addWrappedTertiaries();
+
+        mTask.run();
+
+        RecoverableKeyStoreSecondaryKey secondaryKey = getRecoverableKey(NEXT_SECONDARY_KEY_ALIAS);
+        Map<String, SecretKey> keys =
+                TertiaryKeyStore.newInstance(mApplication, secondaryKey).getAll();
+        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+            SecretKey tertiaryKey = mTertiaryKeysByPackageName.get(packageName);
+            SecretKey newlyWrappedKey = keys.get(packageName);
+            assertThat(tertiaryKey.getEncoded()).isEqualTo(newlyWrappedKey.getEncoded());
+        }
+    }
+
+    @Test
+    public void run_stillSetsActiveSecondaryIfNoTertiaries() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_setsActiveSecondaryKeyAliasInSettings() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_removesNextSecondaryKeyAliasInSettings() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+    }
+
+    @Test
+    public void run_deletesOldKeyFromRecoverableKeyStoreLoader() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mRecoveryController.getKey(CURRENT_SECONDARY_KEY_ALIAS)).isNull();
+    }
+
+    @Test
+    public void run_doesNotRotateIfNoNextAlias() throws Exception {
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+        mCryptoSettings.removeNextSecondaryKeyAlias();
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+    }
+
+    @Test
+    public void run_doesNotRotateIfKeyIsNotSyncedYet() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_doesNotClearNextKeyIfSyncIsJustPending() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get())
+                .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_doesNotRotateIfPermanentFailure() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+                .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+    }
+
+    @Test
+    public void run_removesNextKeyIfPermanentFailure() throws Exception {
+        addNextSecondaryKeyToRecoveryController();
+        setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+        addCurrentSecondaryKeyToRecoveryController();
+        mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+        mBackupServer.setActiveSecondaryKeyAlias(
+                CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+        mTask.run();
+
+        assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+    }
+
+    private void setNextKeyRecoveryStatus(int status) throws Exception {
+        mRecoveryController.setRecoveryStatus(NEXT_SECONDARY_KEY_ALIAS, status);
+    }
+
+    private void addCurrentSecondaryKeyToRecoveryController() throws Exception {
+        mRecoveryController.generateKey(CURRENT_SECONDARY_KEY_ALIAS);
+    }
+
+    private void addNextSecondaryKeyToRecoveryController() throws Exception {
+        mRecoveryController.generateKey(NEXT_SECONDARY_KEY_ALIAS);
+    }
+
+    private void addWrappedTertiaries() throws Exception {
+        TertiaryKeyStore tertiaryKeyStore =
+                TertiaryKeyStore.newInstance(
+                        mApplication, getRecoverableKey(CURRENT_SECONDARY_KEY_ALIAS));
+
+        for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+            tertiaryKeyStore.save(packageName, mTertiaryKeysByPackageName.get(packageName));
+        }
+    }
+
+    private RecoverableKeyStoreSecondaryKey getRecoverableKey(String alias) throws Exception {
+        return mRecoverableSecondaryKeyManager.get(alias).get();
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
index b9055ce..5dfd5ee 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
@@ -18,7 +18,9 @@
 
 import com.android.server.backup.encryption.chunk.ChunkHash;
 import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
 
+import java.nio.charset.Charset;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
 import java.util.Random;
@@ -76,7 +78,10 @@
             int orderingType,
             ChunksMetadataProto.Chunk... chunks) {
         ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
-        chunkListing.fingerprintMixerSalt = Arrays.copyOf(fingerprintSalt, fingerprintSalt.length);
+        chunkListing.fingerprintMixerSalt =
+                fingerprintSalt == null
+                        ? null
+                        : Arrays.copyOf(fingerprintSalt, fingerprintSalt.length);
         chunkListing.cipherType = cipherType;
         chunkListing.chunkOrderingType = orderingType;
         chunkListing.chunks = chunks;
@@ -86,11 +91,33 @@
     public static ChunksMetadataProto.ChunkOrdering newChunkOrdering(
             int[] starts, byte[] checksum) {
         ChunksMetadataProto.ChunkOrdering chunkOrdering = new ChunksMetadataProto.ChunkOrdering();
-        chunkOrdering.starts = Arrays.copyOf(starts, starts.length);
-        chunkOrdering.checksum = Arrays.copyOf(checksum, checksum.length);
+        chunkOrdering.starts = starts == null ? null : Arrays.copyOf(starts, starts.length);
+        chunkOrdering.checksum =
+                checksum == null ? checksum : Arrays.copyOf(checksum, checksum.length);
         return chunkOrdering;
     }
 
+    public static ChunksMetadataProto.ChunksMetadata newChunksMetadata(
+            int cipherType, int checksumType, int chunkOrderingType, byte[] chunkOrdering) {
+        ChunksMetadataProto.ChunksMetadata metadata = new ChunksMetadataProto.ChunksMetadata();
+        metadata.cipherType = cipherType;
+        metadata.checksumType = checksumType;
+        metadata.chunkOrdering = Arrays.copyOf(chunkOrdering, chunkOrdering.length);
+        metadata.chunkOrderingType = chunkOrderingType;
+        return metadata;
+    }
+
+    public static KeyValuePairProto.KeyValuePair newPair(String key, String value) {
+        return newPair(key, value.getBytes(Charset.forName("UTF-8")));
+    }
+
+    public static KeyValuePairProto.KeyValuePair newPair(String key, byte[] value) {
+        KeyValuePairProto.KeyValuePair newPair = new KeyValuePairProto.KeyValuePair();
+        newPair.key = key;
+        newPair.value = value;
+        return newPair;
+    }
+
     public static ChunksMetadataProto.ChunkListing clone(
             ChunksMetadataProto.ChunkListing original) {
         ChunksMetadataProto.Chunk[] clonedChunks;
@@ -114,4 +141,25 @@
     public static ChunksMetadataProto.Chunk clone(ChunksMetadataProto.Chunk original) {
         return newChunk(original.hash, original.length);
     }
+
+    public static ChunksMetadataProto.ChunksMetadata clone(
+            ChunksMetadataProto.ChunksMetadata original) {
+        ChunksMetadataProto.ChunksMetadata cloneMetadata = new ChunksMetadataProto.ChunksMetadata();
+        cloneMetadata.chunkOrderingType = original.chunkOrderingType;
+        cloneMetadata.chunkOrdering =
+                original.chunkOrdering == null
+                        ? null
+                        : Arrays.copyOf(original.chunkOrdering, original.chunkOrdering.length);
+        cloneMetadata.checksumType = original.checksumType;
+        cloneMetadata.cipherType = original.cipherType;
+        return cloneMetadata;
+    }
+
+    public static ChunksMetadataProto.ChunkOrdering clone(
+            ChunksMetadataProto.ChunkOrdering original) {
+        ChunksMetadataProto.ChunkOrdering clone = new ChunksMetadataProto.ChunkOrdering();
+        clone.starts = Arrays.copyOf(original.starts, original.starts.length);
+        clone.checksum = Arrays.copyOf(original.checksum, original.checksum.length);
+        return clone;
+    }
 }
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
new file mode 100644
index 0000000..e5d73ba
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.common.io.ByteStreams;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/** Utility methods for use in tests */
+public class TestFileUtils {
+    /** Read the contents of a file into a byte array */
+    public static byte[] toByteArray(File file) throws IOException {
+        try (FileInputStream fis = new FileInputStream(file)) {
+            return ByteStreams.toByteArray(fis);
+        }
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
new file mode 100644
index 0000000..3329060
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.fakes;
+
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+/** Fake {@link CryptoBackupServer}, for tests. Stores tertiary keys in memory. */
+public class FakeCryptoBackupServer implements CryptoBackupServer {
+    @GuardedBy("this")
+    @Nullable
+    private String mActiveSecondaryKeyAlias;
+
+    // Secondary key alias -> (package name -> tertiary key)
+    @GuardedBy("this")
+    private Map<String, Map<String, WrappedKeyProto.WrappedKey>> mWrappedKeyStore = new HashMap<>();
+
+    @Override
+    public String uploadIncrementalBackup(
+            String packageName,
+            String oldDocId,
+            byte[] diffScript,
+            WrappedKeyProto.WrappedKey tertiaryKey) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String uploadNonIncrementalBackup(
+            String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized void setActiveSecondaryKeyAlias(
+            String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
+        mActiveSecondaryKeyAlias = keyAlias;
+
+        mWrappedKeyStore.putIfAbsent(keyAlias, new HashMap<>());
+        Map<String, WrappedKeyProto.WrappedKey> keyStore = mWrappedKeyStore.get(keyAlias);
+
+        for (String packageName : tertiaryKeys.keySet()) {
+            keyStore.put(packageName, tertiaryKeys.get(packageName));
+        }
+    }
+
+    public synchronized Optional<String> getActiveSecondaryKeyAlias() {
+        return Optional.ofNullable(mActiveSecondaryKeyAlias);
+    }
+
+    public synchronized Map<String, WrappedKeyProto.WrappedKey> getAllTertiaryKeys(
+            String secondaryKeyAlias) throws UnexpectedActiveSecondaryOnServerException {
+        if (!secondaryKeyAlias.equals(mActiveSecondaryKeyAlias)) {
+            throw new UnexpectedActiveSecondaryOnServerException(
+                    String.format(
+                            Locale.US,
+                            "Requested tertiary keys wrapped with %s but %s was active secondary.",
+                            secondaryKeyAlias,
+                            mActiveSecondaryKeyAlias));
+        }
+
+        if (!mWrappedKeyStore.containsKey(secondaryKeyAlias)) {
+            return Collections.emptyMap();
+        }
+        return new HashMap<>(mWrappedKeyStore.get(secondaryKeyAlias));
+    }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
new file mode 100644
index 0000000..4cd8333b
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.fakes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.util.Pair;
+
+import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FakeCryptoBackupServerTest {
+    private static final String PACKAGE_NAME_1 = "package1";
+    private static final String PACKAGE_NAME_2 = "package2";
+    private static final String PACKAGE_NAME_3 = "package3";
+    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_1 = createWrappedKey("key1");
+    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_2 = createWrappedKey("key2");
+    private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_3 = createWrappedKey("key3");
+
+    private FakeCryptoBackupServer mServer;
+
+    @Before
+    public void setUp() {
+        mServer = new FakeCryptoBackupServer();
+    }
+
+    @Test
+    public void getActiveSecondaryKeyAlias_isInitiallyAbsent() throws Exception {
+        assertFalse(mServer.getActiveSecondaryKeyAlias().isPresent());
+    }
+
+    @Test
+    public void setActiveSecondaryKeyAlias_setsTheKeyAlias() throws Exception {
+        String keyAlias = "test";
+        mServer.setActiveSecondaryKeyAlias(keyAlias, Collections.emptyMap());
+        assertThat(mServer.getActiveSecondaryKeyAlias().get()).isEqualTo(keyAlias);
+    }
+
+    @Test
+    public void getAllTertiaryKeys_returnsWrappedKeys() throws Exception {
+        Map<String, WrappedKeyProto.WrappedKey> entries =
+                createKeyMap(
+                        new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
+                        new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2));
+        String secondaryKeyAlias = "doge";
+        mServer.setActiveSecondaryKeyAlias(secondaryKeyAlias, entries);
+
+        assertThat(mServer.getAllTertiaryKeys(secondaryKeyAlias)).containsExactlyEntriesIn(entries);
+    }
+
+    @Test
+    public void addTertiaryKeys_updatesExistingSet() throws Exception {
+        String keyId = "karlin";
+        WrappedKeyProto.WrappedKey replacementKey = createWrappedKey("some replacement bytes");
+
+        mServer.setActiveSecondaryKeyAlias(
+                keyId,
+                createKeyMap(
+                        new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
+                        new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2)));
+
+        mServer.setActiveSecondaryKeyAlias(
+                keyId,
+                createKeyMap(
+                        new Pair<>(PACKAGE_NAME_1, replacementKey),
+                        new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
+
+        assertThat(mServer.getAllTertiaryKeys(keyId))
+                .containsExactlyEntriesIn(
+                        createKeyMap(
+                                new Pair<>(PACKAGE_NAME_1, replacementKey),
+                                new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2),
+                                new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
+    }
+
+    @Test
+    public void getAllTertiaryKeys_throwsForUnknownSecondaryKeyAlias() throws Exception {
+        assertThrows(
+                UnexpectedActiveSecondaryOnServerException.class,
+                () -> mServer.getAllTertiaryKeys("unknown"));
+    }
+
+    @Test
+    public void uploadIncrementalBackup_throwsUnsupportedOperationException() {
+        assertThrows(
+                UnsupportedOperationException.class,
+                () ->
+                        mServer.uploadIncrementalBackup(
+                                PACKAGE_NAME_1,
+                                "docid",
+                                new byte[0],
+                                new WrappedKeyProto.WrappedKey()));
+    }
+
+    @Test
+    public void uploadNonIncrementalBackup_throwsUnsupportedOperationException() {
+        assertThrows(
+                UnsupportedOperationException.class,
+                () ->
+                        mServer.uploadNonIncrementalBackup(
+                                PACKAGE_NAME_1, new byte[0], new WrappedKeyProto.WrappedKey()));
+    }
+
+    private static WrappedKeyProto.WrappedKey createWrappedKey(String data) {
+        WrappedKeyProto.WrappedKey wrappedKey = new WrappedKeyProto.WrappedKey();
+        wrappedKey.key = data.getBytes(Charset.forName("UTF-8"));
+        return wrappedKey;
+    }
+
+    private Map<String, WrappedKeyProto.WrappedKey> createKeyMap(
+            Pair<String, WrappedKeyProto.WrappedKey>... pairs) {
+        Map<String, WrappedKeyProto.WrappedKey> map = new HashMap<>();
+        for (Pair<String, WrappedKeyProto.WrappedKey> pair : pairs) {
+            map.put(pair.first, pair.second);
+        }
+        return map;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index afd722b..447e579 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -22,6 +22,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -34,8 +35,9 @@
     @Inject
     public CarNotificationInterruptionStateProvider(Context context,
             NotificationFilter filter,
-            StatusBarStateController stateController) {
-        super(context, filter, stateController);
+            StatusBarStateController stateController,
+            BatteryController batteryController) {
+        super(context, filter, stateController, batteryController);
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 58f80a4..d79849c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -257,6 +257,11 @@
         return false;
     }
 
+    @Override
+    public boolean isAodPowerSave() {
+        return false;
+    }
+
     private void notifyBatteryLevelChanged() {
         for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) {
             mChangeCallbacks.get(i)
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 6a90671..19c6664 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -865,6 +865,10 @@
         void handleRebuildList() {
             AppFilter filter;
             Comparator<AppEntry> comparator;
+
+            if (!mResumed) {
+                return;
+            }
             synchronized (mRebuildSync) {
                 if (!mRebuildRequested) {
                     return;
@@ -1069,8 +1073,8 @@
                 }
             }
             if (rebuildingSessions != null) {
-                for (int i = 0; i < rebuildingSessions.size(); i++) {
-                    rebuildingSessions.get(i).handleRebuildList();
+                for (Session session : rebuildingSessions) {
+                    session.handleRebuildList();
                 }
             }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 5c9a06f9..4e052f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -26,7 +26,7 @@
 
     private static volatile Thread sMainThread;
     private static volatile Handler sMainThreadHandler;
-    private static volatile ExecutorService sSingleThreadExecutor;
+    private static volatile ExecutorService sThreadExecutor;
 
     /**
      * Returns true if the current thread is the UI thread.
@@ -64,10 +64,11 @@
      * @Return A future of the task that can be monitored for updates or cancelled.
      */
     public static Future postOnBackgroundThread(Runnable runnable) {
-        if (sSingleThreadExecutor == null) {
-            sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+        if (sThreadExecutor == null) {
+            sThreadExecutor = Executors.newFixedThreadPool(
+                    Runtime.getRuntime().availableProcessors());
         }
-        return sSingleThreadExecutor.submit(runnable);
+        return sThreadExecutor.submit(runnable);
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index f8697a1..95a4f69b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -269,7 +269,7 @@
     }
 
     @Test
-    public void testDefaultSessionLoadsAll() {
+    public void testDefaultSession_isResumed_LoadsAll() {
         mSession.onResume();
 
         addApp(HOME_PACKAGE_NAME, 1);
@@ -296,6 +296,19 @@
     }
 
     @Test
+    public void testDefaultSession_isPaused_NotLoadsAll() {
+        mSession.onResume();
+
+        addApp(HOME_PACKAGE_NAME, 1);
+        addApp(LAUNCHABLE_PACKAGE_NAME, 2);
+        mSession.mResumed = false;
+        mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+        processAllMessages();
+
+        verify(mCallbacks, never()).onRebuildComplete(mAppEntriesCaptor.capture());
+    }
+
+    @Test
     public void testCustomSessionLoadsIconsOnly() {
         mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
         mSession.onResume();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 26db124..5114b00 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -50,7 +50,7 @@
     }
 
     @Test
-    public void testPostOnMainThread_shouldRunOnMainTread() {
+    public void testPostOnMainThread_shouldRunOnMainThread() {
         TestRunnable cr = new TestRunnable();
         ShadowLooper.pauseMainLooper();
         ThreadUtils.postOnMainThread(cr);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 046ffc3..2ce4e97 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -76,7 +76,7 @@
                 ConfigSettingsProto.RUNTIME_NATIVE_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
                 ConfigSettingsProto.RUNTIME_NATIVE_BOOT_SETTINGS);
-        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_STORAGE,
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                 ConfigSettingsProto.STORAGE_SETTINGS);
         namespaceToFieldMap.put(DeviceConfig.NAMESPACE_SYSTEMUI,
                 ConfigSettingsProto.SYSTEMUI_SETTINGS);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 3a7de18..86625fa 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -719,7 +719,8 @@
                  Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
                  Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
                  Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
-                 Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
+                 Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED,
+                 Settings.Secure.FACE_UNLOCK_RE_ENROLL);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e767bcc..e11c063 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
@@ -240,7 +241,7 @@
 
         <activity
             android:name=".BugreportWarningActivity"
-            android:theme="@android:style/Theme.DeviceDefault.Dialog.Alert"
+            android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:exported="false" />
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index 56efb49..f2b0606 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -29,6 +29,9 @@
         android:id="@+id/no_carrier_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:gravity="center_vertical"
         android:textAppearance="@style/TextAppearance.QS.Status"
         android:textDirection="locale"
         android:marqueeRepeatLimit="marquee_forever"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
index 4849dfb..7d6ff3b1 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -20,10 +20,10 @@
     android:id="@+id/plugin_frame"
     android:theme="@style/qs_theme"
     android:layout_width="@dimen/qs_panel_width"
-    android:layout_height="96dp"
+    android:layout_height="105dp"
     android:layout_gravity="center_horizontal"
-    android:layout_marginTop="@*android:dimen/quick_qs_total_height"
+    android:layout_marginTop="@dimen/notification_side_paddings"
     android:layout_marginLeft="@dimen/notification_side_paddings"
     android:layout_marginRight="@dimen/notification_side_paddings"
     android:visibility="gone"
-    android:background="@drawable/qs_background_primary"/>
\ No newline at end of file
+    android:background="@drawable/qs_background_primary"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 720074b..a6b3be2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -219,7 +219,7 @@
 
     private final Context mContext;
     private final boolean mIsPrimaryUser;
-    HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
+    HashMap<Integer, SimData> mSimDatas = new HashMap<>();
     HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();
 
     private int mRingMode;
@@ -2512,8 +2512,7 @@
     @MainThread
     public void reportSimUnlocked(int subId) {
         if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")");
-        int slotId = SubscriptionManager.getSlotIndex(subId);
-        handleSimStateChange(subId, slotId, State.READY);
+        handleSimStateChange(subId, getSlotId(subId), State.READY);
     }
 
     /**
@@ -2586,6 +2585,13 @@
         }
     }
 
+    private int getSlotId(int subId) {
+        if (!mSimDatas.containsKey(subId)) {
+            refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
+        }
+        return mSimDatas.get(subId).slotId;
+    }
+
     private final TaskStackChangeListener
             mTaskStackListener = new TaskStackChangeListener() {
         @Override
@@ -2710,7 +2716,7 @@
         for (int i = 0; i < list.size(); i++) {
             final SubscriptionInfo info = list.get(i);
             final int id = info.getSubscriptionId();
-            int slotId = SubscriptionManager.getSlotIndex(id);
+            int slotId = getSlotId(id);
             if (state == getSimState(id) && bestSlotId > slotId) {
                 resultId = id;
                 bestSlotId = slotId;
@@ -2752,7 +2758,7 @@
 
     private void checkIsHandlerThread() {
         if (!mHandler.getLooper().isCurrentThread()) {
-            Log.wtf(TAG, "must call on mHandler's thread "
+            Log.wtfStack(TAG, "must call on mHandler's thread "
                     + mHandler.getLooper().getThread() + ", not " + Thread.currentThread());
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
index 0fa80ac..d9b4297 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
@@ -18,6 +18,8 @@
 
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsModule;
 
 import dagger.Binds;
 import dagger.Module;
@@ -27,7 +29,7 @@
 /**
  * SystemUI objects that are injectable should go here.
  */
-@Module
+@Module(includes = {RecentsModule.class})
 public abstract class SystemUIBinder {
     /** Inject into KeyguardViewMediator. */
     @Binds
@@ -40,4 +42,10 @@
     @IntoMap
     @ClassKey(PowerUI.class)
     public abstract SystemUI bindPowerUI(PowerUI sysui);
+
+    /** Inject into StatusBar. */
+    @Binds
+    @IntoMap
+    @ClassKey(Recents.class)
+    public abstract SystemUI bindRecents(Recents sysui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index bf81e1d..9958124 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -152,7 +152,8 @@
                     .setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
         }
         // Logs assistant invocation cancelled.
-        if (!mInvocationAnimator.isRunning() && invocationWasInProgress && progress == 0f) {
+        if ((mInvocationAnimator == null || !mInvocationAnimator.isRunning())
+                && invocationWasInProgress && progress == 0f) {
             if (VERBOSE) {
                 Log.v(TAG, "Invocation cancelled: type=" + type);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 1d7e9ea..419fd62 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -82,7 +82,7 @@
 
     private void requestPulse(State dozeState) {
         if (!mDozeHost.isPulsingBlocked() && dozeState.canPulse()) {
-            mMachine.requestPulse(DozeLog.PULSE_REASON_DOCKING);
+            mMachine.requestPulse(DozeEvent.PULSE_REASON_DOCKING);
         }
         mPulsePending = false;
     }
@@ -91,7 +91,7 @@
         if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING
                 || dozeState == State.DOZE_PULSING_BRIGHT) {
             final int pulseReason = mMachine.getPulseReason();
-            if (pulseReason == DozeLog.PULSE_REASON_DOCKING) {
+            if (pulseReason == DozeEvent.PULSE_REASON_DOCKING) {
                 mDozeHost.stopPulsing();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
new file mode 100644
index 0000000..ea1def0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import android.annotation.IntDef;
+
+import com.android.systemui.log.RichEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event related to dozing. {@link DozeLog} stores and prints these events for debugging
+ * and triaging purposes.
+ */
+public class DozeEvent extends RichEvent {
+    public static final int TOTAL_EVENT_TYPES = 19;
+
+    public DozeEvent(int logLevel, int type, String reason) {
+        super(logLevel, type, reason);
+    }
+
+    /**
+     * Event labels for each doze event
+     * Index corresponds to the integer associated with each {@link EventType}
+     */
+    @Override
+    public String[] getEventLabels() {
+        final String[] events = new String[]{
+                "PickupWakeup",
+                "PulseStart",
+                "PulseFinish",
+                "NotificationPulse",
+                "Dozing",
+                "Fling",
+                "EmergencyCall",
+                "KeyguardBouncerChanged",
+                "ScreenOn",
+                "ScreenOff",
+                "MissedTick",
+                "TimeTickScheduled",
+                "KeyguardVisibilityChanged",
+                "DozeStateChanged",
+                "WakeDisplay",
+                "ProximityResult",
+                "PulseDropped",
+                "PulseDisabledByProx",
+                "SensorTriggered"
+        };
+
+        if (events.length != TOTAL_EVENT_TYPES) {
+            throw new IllegalStateException("DozeEvent events.length should match TOTAL_EVENT_TYPES"
+                    + " events.length=" + events.length
+                    + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
+        }
+        return events;
+    }
+
+    /**
+     * Converts the reason (integer) to a user-readable string
+     */
+    public static String reasonToString(@Reason int pulseReason) {
+        switch (pulseReason) {
+            case PULSE_REASON_INTENT: return "intent";
+            case PULSE_REASON_NOTIFICATION: return "notification";
+            case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
+            case REASON_SENSOR_PICKUP: return "pickup";
+            case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+            case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
+            case PULSE_REASON_DOCKING: return "docking";
+            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
+            case REASON_SENSOR_WAKE_UP: return "wakeup";
+            case REASON_SENSOR_TAP: return "tap";
+            default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
+        }
+    }
+
+    /**
+     * Builds a DozeEvent.
+     */
+    public static class DozeEventBuilder extends RichEvent.Builder<DozeEventBuilder> {
+        @Override
+        public DozeEventBuilder getBuilder() {
+            return this;
+        }
+
+        @Override
+        public RichEvent build() {
+            return new DozeEvent(mLogLevel, mType, mReason);
+        }
+    }
+
+    @IntDef({PICKUP_WAKEUP, PULSE_START, PULSE_FINISH, NOTIFICATION_PULSE, DOZING, FLING,
+            EMERGENCY_CALL, KEYGUARD_BOUNCER_CHANGED, SCREEN_ON, SCREEN_OFF, MISSED_TICK,
+            TIME_TICK_SCHEDULED, KEYGUARD_VISIBILITY_CHANGE, DOZE_STATE_CHANGED, WAKE_DISPLAY,
+            PROXIMITY_RESULT, PULSE_DROPPED, PULSE_DISABLED_BY_PROX, SENSOR_TRIGGERED})
+    /**
+     * Types of DozeEvents
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {}
+    public static final int PICKUP_WAKEUP = 0;
+    public static final int PULSE_START = 1;
+    public static final int PULSE_FINISH = 2;
+    public static final int NOTIFICATION_PULSE = 3;
+    public static final int DOZING = 4;
+    public static final int FLING = 5;
+    public static final int EMERGENCY_CALL = 6;
+    public static final int KEYGUARD_BOUNCER_CHANGED = 7;
+    public static final int SCREEN_ON = 8;
+    public static final int SCREEN_OFF = 9;
+    public static final int MISSED_TICK = 10;
+    public static final int TIME_TICK_SCHEDULED = 11;
+    public static final int KEYGUARD_VISIBILITY_CHANGE = 12;
+    public static final int DOZE_STATE_CHANGED = 13;
+    public static final int WAKE_DISPLAY = 14;
+    public static final int PROXIMITY_RESULT = 15;
+    public static final int PULSE_DROPPED = 16;
+    public static final int PULSE_DISABLED_BY_PROX = 17;
+    public static final int SENSOR_TRIGGERED = 18;
+
+    public static final int TOTAL_REASONS = 10;
+    @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
+            PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
+            PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
+            PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Reason {}
+    public static final int PULSE_REASON_NONE = -1;
+    public static final int PULSE_REASON_INTENT = 0;
+    public static final int PULSE_REASON_NOTIFICATION = 1;
+    public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
+    public static final int REASON_SENSOR_PICKUP = 3;
+    public static final int REASON_SENSOR_DOUBLE_TAP = 4;
+    public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
+    public static final int PULSE_REASON_DOCKING = 6;
+    public static final int REASON_SENSOR_WAKE_UP = 7;
+    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
+    public static final int REASON_SENSOR_TAP = 9;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 4d3dc70..ca4ec6d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -44,7 +45,8 @@
     }
 
     /** Creates a DozeMachine with its parts for {@code dozeService}. */
-    public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) {
+    public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager,
+            DozeLog dozeLog) {
         Context context = dozeService;
         AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
         AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
@@ -65,13 +67,14 @@
                 params);
 
         DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
-                wakefulnessLifecycle);
+                wakefulnessLifecycle, Dependency.get(BatteryController.class), dozeLog);
         machine.setParts(new DozeMachine.Part[]{
                 new DozePauser(handler, machine, alarmManager, params.getPolicy()),
                 new DozeFalsingManagerAdapter(falsingManager),
                 createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
-                        handler, wakeLock, machine, dockManager),
-                createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
+                        handler, wakeLock, machine, dockManager, dozeLog),
+                createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params,
+                        dozeLog),
                 new DozeScreenState(wrappedService, handler, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
                         handler),
@@ -95,18 +98,19 @@
     private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
             DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
             DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
-            DockManager dockManager) {
+            DockManager dockManager, DozeLog dozeLog) {
         boolean allowPulseTriggers = true;
         return new DozeTriggers(context, machine, host, alarmManager, config, params,
                 sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
-                new ProximitySensor(context, sensorManager));
+                new ProximitySensor(context, sensorManager), dozeLog);
+
     }
 
     private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
             DozeMachine machine, Handler handler, AlarmManager alarmManager,
-            DozeParameters params) {
+            DozeParameters params, DozeLog dozeLog) {
         return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
-                Dependency.get(KeyguardUpdateMonitor.class));
+                Dependency.get(KeyguardUpdateMonitor.class), dozeLog);
     }
 
     public static DozeHost getHost(DozeService service) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 8fe9f92..2e4466d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -16,282 +16,284 @@
 
 package com.android.systemui.doze;
 
-import android.content.Context;
-import android.os.Build;
-import android.util.Log;
 import android.util.TimeUtils;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.log.SysuiLog;
 
 import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
 import java.util.Date;
 
-public class DozeLog {
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand:
+ *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ *      dependency DumpController DozeLog
+ */
+@Singleton
+public class DozeLog extends SysuiLog {
     private static final String TAG = "DozeLog";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final boolean ENABLED = true;
-    private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
-    static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    public static final int REASONS = 10;
+    private boolean mPulsing;
+    private long mSince;
+    private SummaryStats mPickupPulseNearVibrationStats;
+    private SummaryStats mPickupPulseNotNearVibrationStats;
+    private SummaryStats mNotificationPulseStats;
+    private SummaryStats mScreenOnPulsingStats;
+    private SummaryStats mScreenOnNotPulsingStats;
+    private SummaryStats mEmergencyCallStats;
+    private SummaryStats[][] mProxStats; // [reason][near/far]
 
-    public static final int PULSE_REASON_NONE = -1;
-    public static final int PULSE_REASON_INTENT = 0;
-    public static final int PULSE_REASON_NOTIFICATION = 1;
-    public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
-    public static final int REASON_SENSOR_PICKUP = 3;
-    public static final int REASON_SENSOR_DOUBLE_TAP = 4;
-    public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
-    public static final int PULSE_REASON_DOCKING = 6;
-    public static final int REASON_SENSOR_WAKE_UP = 7;
-    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
-    public static final int REASON_SENSOR_TAP = 9;
+    @Inject
+    public DozeLog(KeyguardUpdateMonitor keyguardUpdateMonitor, DumpController dumpController) {
+        super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
+        mSince = System.currentTimeMillis();
+        mPickupPulseNearVibrationStats = new SummaryStats();
+        mPickupPulseNotNearVibrationStats = new SummaryStats();
+        mNotificationPulseStats = new SummaryStats();
+        mScreenOnPulsingStats = new SummaryStats();
+        mScreenOnNotPulsingStats = new SummaryStats();
+        mEmergencyCallStats = new SummaryStats();
+        mProxStats = new SummaryStats[DozeEvent.TOTAL_REASONS][2];
+        for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+            mProxStats[i][0] = new SummaryStats();
+            mProxStats[i][1] = new SummaryStats();
+        }
 
-    private static boolean sRegisterKeyguardCallback = true;
-
-    private static long[] sTimes;
-    private static String[] sMessages;
-    private static int sPosition;
-    private static int sCount;
-    private static boolean sPulsing;
-
-    private static long sSince;
-    private static SummaryStats sPickupPulseNearVibrationStats;
-    private static SummaryStats sPickupPulseNotNearVibrationStats;
-    private static SummaryStats sNotificationPulseStats;
-    private static SummaryStats sScreenOnPulsingStats;
-    private static SummaryStats sScreenOnNotPulsingStats;
-    private static SummaryStats sEmergencyCallStats;
-    private static SummaryStats[][] sProxStats; // [reason][near/far]
-
-    public static void tracePickupWakeUp(Context context, boolean withinVibrationThreshold) {
-        if (!ENABLED) return;
-        init(context);
-        log("pickupWakeUp withinVibrationThreshold=" + withinVibrationThreshold);
-        (withinVibrationThreshold ? sPickupPulseNearVibrationStats
-                : sPickupPulseNotNearVibrationStats).append();
-    }
-
-    public static void tracePulseStart(int reason) {
-        if (!ENABLED) return;
-        sPulsing = true;
-        log("pulseStart reason=" + reasonToString(reason));
-    }
-
-    public static void tracePulseFinish() {
-        if (!ENABLED) return;
-        sPulsing = false;
-        log("pulseFinish");
-    }
-
-    public static void traceNotificationPulse(Context context) {
-        if (!ENABLED) return;
-        init(context);
-        log("notificationPulse");
-        sNotificationPulseStats.append();
-    }
-
-    private static void init(Context context) {
-        synchronized (DozeLog.class) {
-            if (sMessages == null) {
-                sTimes = new long[SIZE];
-                sMessages = new String[SIZE];
-                sSince = System.currentTimeMillis();
-                sPickupPulseNearVibrationStats = new SummaryStats();
-                sPickupPulseNotNearVibrationStats = new SummaryStats();
-                sNotificationPulseStats = new SummaryStats();
-                sScreenOnPulsingStats = new SummaryStats();
-                sScreenOnNotPulsingStats = new SummaryStats();
-                sEmergencyCallStats = new SummaryStats();
-                sProxStats = new SummaryStats[REASONS][2];
-                for (int i = 0; i < REASONS; i++) {
-                    sProxStats[i][0] = new SummaryStats();
-                    sProxStats[i][1] = new SummaryStats();
-                }
-                log("init");
-                if (sRegisterKeyguardCallback) {
-                    Dependency.get(KeyguardUpdateMonitor.class).registerCallback(sKeyguardCallback);
-                }
-            }
+        if (keyguardUpdateMonitor != null) {
+            keyguardUpdateMonitor.registerCallback(mKeyguardCallback);
         }
     }
 
-    public static void traceDozing(Context context, boolean dozing) {
-        if (!ENABLED) return;
-        sPulsing = false;
-        init(context);
-        log("dozing " + dozing);
+    /**
+     * Appends pickup wakeup event to the logs
+     */
+    public void tracePickupWakeUp(boolean withinVibrationThreshold) {
+        if (log(DozeEvent.PICKUP_WAKEUP,
+                "withinVibrationThreshold=" + withinVibrationThreshold)) {
+            (withinVibrationThreshold ? mPickupPulseNearVibrationStats
+                    : mPickupPulseNotNearVibrationStats).append();
+        }
     }
 
-    public static void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
+    /**
+     * Appends pulse started event to the logs.
+     * @param reason why the pulse started
+     */
+    public void tracePulseStart(@DozeEvent.Reason int reason) {
+        if (log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason))) {
+            mPulsing = true;
+        }
+    }
+
+    /**
+     * Appends pulse finished event to the logs
+     */
+    public void tracePulseFinish() {
+        if (log(DozeEvent.PULSE_FINISH)) {
+            mPulsing = false;
+        }
+    }
+
+    /**
+     * Appends pulse event to the logs
+     */
+    public void traceNotificationPulse() {
+        if (log(DozeEvent.NOTIFICATION_PULSE)) {
+            mNotificationPulseStats.append();
+        }
+    }
+
+    /**
+     * Appends dozing event to the logs
+     * @param dozing true if dozing, else false
+     */
+    public void traceDozing(boolean dozing) {
+        if (log(DozeEvent.DOZING, "dozing=" + dozing)) {
+            mPulsing = false;
+        }
+    }
+
+    /**
+     * Appends fling event to the logs
+     */
+    public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
             boolean screenOnFromTouch) {
-        if (!ENABLED) return;
-        log("fling expand=" + expand + " aboveThreshold=" + aboveThreshold + " thresholdNeeded="
-                + thresholdNeeded + " screenOnFromTouch=" + screenOnFromTouch);
+        log(DozeEvent.FLING, "expand=" + expand
+                + " aboveThreshold=" + aboveThreshold
+                + " thresholdNeeded=" + thresholdNeeded
+                + " screenOnFromTouch=" + screenOnFromTouch);
     }
 
-    public static void traceEmergencyCall() {
-        if (!ENABLED) return;
-        log("emergencyCall");
-        sEmergencyCallStats.append();
-    }
-
-    public static void traceKeyguardBouncerChanged(boolean showing) {
-        if (!ENABLED) return;
-        log("bouncer " + showing);
-    }
-
-    public static void traceScreenOn() {
-        if (!ENABLED) return;
-        log("screenOn pulsing=" + sPulsing);
-        (sPulsing ? sScreenOnPulsingStats : sScreenOnNotPulsingStats).append();
-        sPulsing = false;
-    }
-
-    public static void traceScreenOff(int why) {
-        if (!ENABLED) return;
-        log("screenOff why=" + why);
-    }
-
-    public static void traceMissedTick(String delay) {
-        if (!ENABLED) return;
-        log("missedTick by=" + delay);
-    }
-
-    public static void traceTimeTickScheduled(long when, long triggerAt) {
-        if (!ENABLED) return;
-        log("timeTickScheduled at=" + FORMAT.format(new Date(when)) + " triggerAt="
-                + FORMAT.format(new Date(triggerAt)));
-    }
-
-    public static void traceKeyguard(boolean showing) {
-        if (!ENABLED) return;
-        log("keyguard " + showing);
-        if (!showing) {
-            sPulsing = false;
+    /**
+     * Appends emergency call event to the logs
+     */
+    public void traceEmergencyCall() {
+        if (log(DozeEvent.EMERGENCY_CALL)) {
+            mEmergencyCallStats.append();
         }
     }
 
-    public static void traceState(DozeMachine.State state) {
-        if (!ENABLED) return;
-        log("state " + state);
+    /**
+     * Appends keyguard bouncer changed event to the logs
+     * @param showing true if the keyguard bouncer is showing, else false
+     */
+    public void traceKeyguardBouncerChanged(boolean showing) {
+        log(DozeEvent.KEYGUARD_BOUNCER_CHANGED, "showing=" + showing);
+    }
+
+    /**
+     * Appends screen-on event to the logs
+     */
+    public void traceScreenOn() {
+        if (log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing)) {
+            (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append();
+            mPulsing = false;
+        }
+    }
+
+    /**
+     * Appends screen-off event to the logs
+     * @param why reason the screen is off
+     */
+    public void traceScreenOff(int why) {
+        log(DozeEvent.SCREEN_OFF, "why=" + why);
+    }
+
+    /**
+     * Appends missed tick event to the logs
+     * @param delay of the missed tick
+     */
+    public void traceMissedTick(String delay) {
+        log(DozeEvent.MISSED_TICK, "delay=" + delay);
+    }
+
+    /**
+     * Appends time tick scheduled event to the logs
+     * @param when time tick scheduled at
+     * @param triggerAt time tick trigger at
+     */
+    public void traceTimeTickScheduled(long when, long triggerAt) {
+        log(DozeEvent.TIME_TICK_SCHEDULED,
+                "scheduledAt=" + DATE_FORMAT.format(new Date(when))
+                + " triggerAt=" + DATE_FORMAT.format(new Date(triggerAt)));
+    }
+
+    /**
+     * Appends keyguard visibility change event to the logs
+     * @param showing whether the keyguard is now showing
+     */
+    public void traceKeyguard(boolean showing) {
+        if (log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing)
+                && !showing) {
+            mPulsing = false;
+        }
+    }
+
+    /**
+     * Appends doze state changed event to the logs
+     * @param state new DozeMachine state
+     */
+    public void traceState(DozeMachine.State state) {
+        log(DozeEvent.DOZE_STATE_CHANGED, state.name());
     }
 
     /**
      * Appends wake-display event to the logs.
      * @param wake if we're waking up or sleeping.
      */
-    public static void traceWakeDisplay(boolean wake) {
-        if (!ENABLED) return;
-        log("wakeDisplay " + wake);
+    public void traceWakeDisplay(boolean wake) {
+        log(DozeEvent.WAKE_DISPLAY, "wake=" + wake);
     }
 
-    public static void traceProximityResult(Context context, boolean near, long millis,
-            int reason) {
-        if (!ENABLED) return;
-        init(context);
-        log("proximityResult reason=" + reasonToString(reason) + " near=" + near
-                + " millis=" + millis);
-        sProxStats[reason][near ? 0 : 1].append();
-    }
-
-    public static String reasonToString(int pulseReason) {
-        switch (pulseReason) {
-            case PULSE_REASON_INTENT: return "intent";
-            case PULSE_REASON_NOTIFICATION: return "notification";
-            case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
-            case REASON_SENSOR_PICKUP: return "pickup";
-            case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
-            case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
-            case PULSE_REASON_DOCKING: return "docking";
-            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
-            case REASON_SENSOR_WAKE_UP: return "wakeup";
-            case REASON_SENSOR_TAP: return "tap";
-            default: throw new IllegalArgumentException("bad reason: " + pulseReason);
+    /**
+     * Appends proximity result event to the logs
+     * @param near true if near, else false
+     * @param millis
+     * @param reason why proximity result was triggered
+     */
+    public void traceProximityResult(boolean near, long millis, @DozeEvent.Reason int reason) {
+        if (log(DozeEvent.PROXIMITY_RESULT,
+                " reason=" + DozeEvent.reasonToString(reason)
+                + " near=" + near
+                + " millis=" + millis)) {
+            mProxStats[reason][near ? 0 : 1].append();
         }
     }
 
-    public static void dump(PrintWriter pw) {
+    /**
+     * Prints doze log timeline and consolidated stats
+     * @param pw
+     */
+    public void dump(PrintWriter pw) {
         synchronized (DozeLog.class) {
-            if (sMessages == null) return;
-            pw.println("  Doze log:");
-            final int start = (sPosition - sCount + SIZE) % SIZE;
-            for (int i = 0; i < sCount; i++) {
-                final int j = (start + i) % SIZE;
-                pw.print("    ");
-                pw.print(FORMAT.format(new Date(sTimes[j])));
-                pw.print(' ');
-                pw.println(sMessages[j]);
-            }
+            super.dump(null, pw, null); // prints timeline
+
             pw.print("  Doze summary stats (for ");
-            TimeUtils.formatDuration(System.currentTimeMillis() - sSince, pw);
+            TimeUtils.formatDuration(System.currentTimeMillis() - mSince, pw);
             pw.println("):");
-            sPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
-            sPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
-            sNotificationPulseStats.dump(pw, "Notification pulse");
-            sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
-            sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
-            sEmergencyCallStats.dump(pw, "Emergency call");
-            for (int i = 0; i < REASONS; i++) {
-                final String reason = reasonToString(i);
-                sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
-                sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
+            mPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
+            mPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
+            mNotificationPulseStats.dump(pw, "Notification pulse");
+            mScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
+            mScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
+            mEmergencyCallStats.dump(pw, "Emergency call");
+            for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+                final String reason = DozeEvent.reasonToString(i);
+                mProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
+                mProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
             }
         }
     }
 
-    private static void log(String msg) {
-        synchronized (DozeLog.class) {
-            if (sMessages == null) return;
-            sTimes[sPosition] = System.currentTimeMillis();
-            sMessages[sPosition] = msg;
-            sPosition = (sPosition + 1) % SIZE;
-            sCount = Math.min(sCount + 1, SIZE);
-        }
-        if (DEBUG) Log.d(TAG, msg);
+    private boolean log(@DozeEvent.EventType int eventType) {
+        return log(eventType, "");
     }
 
-    public static void tracePulseDropped(Context context, boolean pulsePending,
-            DozeMachine.State state, boolean blocked) {
-        if (!ENABLED) return;
-        init(context);
-        log("pulseDropped pulsePending=" + pulsePending + " state="
-                + state + " blocked=" + blocked);
+    private boolean log(@DozeEvent.EventType int eventType, String msg) {
+        return super.log(new DozeEvent.DozeEventBuilder()
+                .setType(eventType)
+                .setReason(msg)
+                .build());
     }
 
-    public static void tracePulseDropped(Context context, String why) {
-        if (!ENABLED) return;
-        init(context);
-        log("pulseDropped why=" + why);
+    /**
+     * Appends pulse dropped event to logs
+     */
+    public void tracePulseDropped(boolean pulsePending, DozeMachine.State state, boolean blocked) {
+        log(DozeEvent.PULSE_DROPPED, "pulsePending=" + pulsePending + " state="
+                + state.name() + " blocked=" + blocked);
     }
 
-    public static void tracePulseTouchDisabledByProx(Context context, boolean disabled) {
-        if (!ENABLED) return;
-        init(context);
-        log("pulseTouchDisabledByProx " + disabled);
+    /**
+     * Appends pulse dropped event to logs
+     * @param reason why the pulse was dropped
+     */
+    public void tracePulseDropped(String reason) {
+        log(DozeEvent.PULSE_DROPPED, "why=" + reason);
     }
 
-    public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) {
-        if (!ENABLED) return;
-        synchronized (DozeLog.class) {
-            if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) {
-                throw new IllegalStateException("Cannot change setRegisterKeyguardCallback "
-                        + "after init()");
-            }
-            sRegisterKeyguardCallback = registerKeyguardCallback;
-        }
+    /**
+     * Appends pulse touch displayed by prox sensor event to logs
+     * @param disabled
+     */
+    public void tracePulseTouchDisabledByProx(boolean disabled) {
+        log(DozeEvent.PULSE_DISABLED_BY_PROX, "disabled=" + disabled);
     }
 
-    public static void traceSensor(Context context, int reason) {
-        if (!ENABLED) return;
-        init(context);
-        log("sensor type=" + reasonToString(reason));
+    /**
+     * Appends sensor triggered event to logs
+     * @param reason why the sensor was triggered
+     */
+    public void traceSensor(@DozeEvent.Reason int reason) {
+        log(DozeEvent.SENSOR_TRIGGERED, "type=" + DozeEvent.reasonToString(reason));
     }
 
-    private static class SummaryStats {
+    private class SummaryStats {
         private int mCount;
 
         public void append() {
@@ -305,7 +307,7 @@
             pw.print(": n=");
             pw.print(mCount);
             pw.print(" (");
-            final double perHr = (double) mCount / (System.currentTimeMillis() - sSince)
+            final double perHr = (double) mCount / (System.currentTimeMillis() - mSince)
                     * 1000 * 60 * 60;
             pw.print(perHr);
             pw.print("/hr)");
@@ -313,7 +315,7 @@
         }
     }
 
-    private static final KeyguardUpdateMonitorCallback sKeyguardCallback =
+    private final KeyguardUpdateMonitorCallback mKeyguardCallback =
             new KeyguardUpdateMonitorCallback() {
         @Override
         public void onEmergencyCallAction() {
@@ -340,4 +342,7 @@
             traceKeyguard(showing);
         }
     };
+
+    private static final int MAX_DOZE_DEBUG_LOGS = 400;
+    private static final int MAX_DOZE_LOGS = 50;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 93a51cc..75b1d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -27,6 +27,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.wakelock.WakeLock;
 
@@ -45,6 +46,7 @@
 
     static final String TAG = "DozeMachine";
     static final boolean DEBUG = DozeService.DEBUG;
+    private final DozeLog mDozeLog;
     private static final String REASON_CHANGE_STATE = "DozeMachine#requestState";
     private static final String REASON_HELD_FOR_STATE = "DozeMachine#heldForState";
 
@@ -121,6 +123,7 @@
     private final WakeLock mWakeLock;
     private final AmbientDisplayConfiguration mConfig;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final BatteryController mBatteryController;
     private Part[] mParts;
 
     private final ArrayList<State> mQueuedRequests = new ArrayList<>();
@@ -129,11 +132,14 @@
     private boolean mWakeLockHeldForCurrentState = false;
 
     public DozeMachine(Service service, AmbientDisplayConfiguration config,
-            WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) {
+            WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+            BatteryController batteryController, DozeLog dozeLog) {
         mDozeService = service;
         mConfig = config;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mWakeLock = wakeLock;
+        mBatteryController = batteryController;
+        mDozeLog = dozeLog;
     }
 
     /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
@@ -155,7 +161,7 @@
     @MainThread
     public void requestState(State requestedState) {
         Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE);
-        requestState(requestedState, DozeLog.PULSE_REASON_NONE);
+        requestState(requestedState, DozeEvent.PULSE_REASON_NONE);
     }
 
     @MainThread
@@ -243,7 +249,7 @@
         State oldState = mState;
         mState = newState;
 
-        DozeLog.traceState(newState);
+        mDozeLog.traceState(newState);
         Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal());
 
         updatePulseReason(newState, oldState, pulseReason);
@@ -257,7 +263,7 @@
         if (newState == State.DOZE_REQUEST_PULSE) {
             mPulseReason = pulseReason;
         } else if (oldState == State.DOZE_PULSE_DONE) {
-            mPulseReason = DozeLog.PULSE_REASON_NONE;
+            mPulseReason = DozeEvent.PULSE_REASON_NONE;
         }
     }
 
@@ -316,6 +322,9 @@
             Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
             return mState;
         }
+        if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) {
+            return State.DOZE;
+        }
         if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
             Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
             return mState;
@@ -349,7 +358,7 @@
                     nextState = State.DOZE;
                 }
 
-                transitionTo(nextState, DozeLog.PULSE_REASON_NONE);
+                transitionTo(nextState, DozeEvent.PULSE_REASON_NONE);
                 break;
             default:
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index a201400..7d86028 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -80,7 +80,8 @@
 
     public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
-            Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
+            Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy,
+            DozeLog dozeLog) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -97,52 +98,59 @@
                         mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
                         null /* setting */,
                         dozeParameters.getPulseOnSigMotion(),
-                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
-                        false /* touchscreen */),
+                        DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
+                        false /* touchscreen */, dozeLog),
                 mPickupSensor = new TriggerSensor(
                         mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
                         Settings.Secure.DOZE_PICK_UP_GESTURE,
                         true /* settingDef */,
                         config.dozePickupSensorAvailable(),
-                        DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
+                        DozeEvent.REASON_SENSOR_PICKUP, false /* touchCoords */,
                         false /* touchscreen */,
-                        false /* ignoresSetting */),
+                        false /* ignoresSetting */,
+                        dozeLog),
                 new TriggerSensor(
                         findSensorWithType(config.doubleTapSensorType()),
                         Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
                         true /* configured */,
-                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
+                        DozeEvent.REASON_SENSOR_DOUBLE_TAP,
                         dozeParameters.doubleTapReportsTouchCoordinates(),
-                        true /* touchscreen */),
+                        true /* touchscreen */,
+                        dozeLog),
                 new TriggerSensor(
                         findSensorWithType(config.tapSensorType()),
                         Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
                         true /* configured */,
-                        DozeLog.REASON_SENSOR_TAP,
+                        DozeEvent.REASON_SENSOR_TAP,
                         false /* reports touch coordinates */,
-                        true /* touchscreen */),
+                        true /* touchscreen */,
+                        dozeLog),
                 new TriggerSensor(
                         findSensorWithType(config.longPressSensorType()),
                         Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                         false /* settingDef */,
                         true /* configured */,
-                        DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
+                        DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
                         true /* reports touch coordinates */,
-                        true /* touchscreen */),
+                        true /* touchscreen */,
+                        dozeLog),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
                         Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                         mConfig.wakeScreenGestureAvailable() && alwaysOn,
-                        DozeLog.REASON_SENSOR_WAKE_UP,
+                        DozeEvent.REASON_SENSOR_WAKE_UP,
                         false /* reports touch coordinates */,
-                        false /* touchscreen */),
+                        false /* touchscreen */,
+                        dozeLog),
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
                         Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
                         mConfig.wakeScreenGestureAvailable(),
-                        DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
+                        DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
                         false /* reports touch coordinates */,
-                        false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
+                        false /* touchscreen */,
+                        mConfig.getWakeLockScreenDebounce(),
+                        dozeLog),
         };
 
         mProximitySensor = new ProximitySensor(context, sensorManager);
@@ -306,23 +314,24 @@
         protected boolean mRegistered;
         protected boolean mDisabled;
         protected boolean mIgnoresSetting;
+        protected final DozeLog mDozeLog;
 
         public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
-                boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+                boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog) {
             this(sensor, setting, true /* settingDef */, configured, pulseReason,
-                    reportsTouchCoordinates, requiresTouchscreen);
+                    reportsTouchCoordinates, requiresTouchscreen, dozeLog);
         }
 
         public TriggerSensor(Sensor sensor, String setting, boolean settingDef,
                 boolean configured, int pulseReason, boolean reportsTouchCoordinates,
-                boolean requiresTouchscreen) {
+                boolean requiresTouchscreen, DozeLog dozeLog) {
             this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates,
-                    requiresTouchscreen, false /* ignoresSetting */);
+                    requiresTouchscreen, false /* ignoresSetting */, dozeLog);
         }
 
         private TriggerSensor(Sensor sensor, String setting, boolean settingDef,
                 boolean configured, int pulseReason, boolean reportsTouchCoordinates,
-                boolean requiresTouchscreen, boolean ignoresSetting) {
+                boolean requiresTouchscreen, boolean ignoresSetting, DozeLog dozeLog) {
             mSensor = sensor;
             mSetting = setting;
             mSettingDefault = settingDef;
@@ -331,6 +340,7 @@
             mReportsTouchCoordinates = reportsTouchCoordinates;
             mRequiresTouchscreen = requiresTouchscreen;
             mIgnoresSetting = ignoresSetting;
+            mDozeLog = dozeLog;
         }
 
         public void setListening(boolean listen) {
@@ -387,7 +397,7 @@
         @Override
         @AnyThread
         public void onTrigger(TriggerEvent event) {
-            DozeLog.traceSensor(mContext, mPulseReason);
+            mDozeLog.traceSensor(mPulseReason);
             mHandler.post(mWakeLock.wrap(() -> {
                 if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
                 if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
@@ -443,16 +453,17 @@
         private long mDebounce;
 
         PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
-                int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+                int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
+                DozeLog dozeLog) {
             this(sensor, setting, configured, pulseReason, reportsTouchCoordinates,
-                    requiresTouchscreen, 0L /* debounce */);
+                    requiresTouchscreen, 0L /* debounce */, dozeLog);
         }
 
         PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
                 int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
-                long debounce) {
+                long debounce, DozeLog dozeLog) {
             super(null, setting, configured, pulseReason, reportsTouchCoordinates,
-                    requiresTouchscreen);
+                    requiresTouchscreen, dozeLog);
             mPluginSensor = sensor;
             mDebounce = debounce;
         }
@@ -498,7 +509,7 @@
 
         @Override
         public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
-            DozeLog.traceSensor(mContext, mPulseReason);
+            mDozeLog.traceSensor(mPulseReason);
             mHandler.post(mWakeLock.wrap(() -> {
                 final long now = SystemClock.uptimeMillis();
                 if (now < mDebounceFrom + mDebounce) {
@@ -515,7 +526,7 @@
 
         /**
          * Called when a sensor requests a pulse
-         * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
+         * @param pulseReason Requesting sensor, e.g. {@link DozeEvent#REASON_SENSOR_PICKUP}
          * @param screenX the location on the screen where the sensor fired or -1
          *                if the sensor doesn't support reporting screen locations.
          * @param screenY the location on the screen where the sensor fired or -1
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index e92acfc..17559c9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -39,15 +39,17 @@
     private static final String TAG = "DozeService";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private final FalsingManager mFalsingManager;
+    private final DozeLog mDozeLog;
 
     private DozeMachine mDozeMachine;
     private DozeServicePlugin mDozePlugin;
     private PluginManager mPluginManager;
 
     @Inject
-    public DozeService(FalsingManager falsingManager) {
+    public DozeService(FalsingManager falsingManager, DozeLog dozeLog) {
         setDebug(DEBUG);
         mFalsingManager = falsingManager;
+        mDozeLog = dozeLog;
     }
 
     @Override
@@ -62,7 +64,7 @@
         }
         mPluginManager = Dependency.get(PluginManager.class);
         mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
-        mDozeMachine = new DozeFactory().assembleMachine(this, mFalsingManager);
+        mDozeMachine = new DozeFactory().assembleMachine(this, mFalsingManager, mDozeLog);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 8eed71c..b212884 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -67,6 +67,7 @@
 
     private final Context mContext;
     private final DozeMachine mMachine;
+    private final DozeLog mDozeLog;
     private final DozeSensors mDozeSensors;
     private final DozeHost mDozeHost;
     private final AmbientDisplayConfiguration mConfig;
@@ -89,7 +90,8 @@
             AlarmManager alarmManager, AmbientDisplayConfiguration config,
             DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
             WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
-            ProximitySensor proximitySensor) {
+            ProximitySensor proximitySensor,
+            DozeLog dozeLog) {
         mContext = context;
         mMachine = machine;
         mDozeHost = dozeHost;
@@ -100,10 +102,11 @@
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
                 config, wakeLock, this::onSensor, this::onProximityFar,
-                dozeParameters.getPolicy());
+                dozeParameters.getPolicy(), dozeLog);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mDockManager = dockManager;
         mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
+        mDozeLog = dozeLog;
     }
 
     private void onNotification(Runnable onPulseSuppressedListener) {
@@ -113,18 +116,18 @@
         if (!sWakeDisplaySensorState) {
             Log.d(TAG, "Wake display false. Pulse denied.");
             runIfNotNull(onPulseSuppressedListener);
-            DozeLog.tracePulseDropped(mContext, "wakeDisplaySensor");
+            mDozeLog.tracePulseDropped("wakeDisplaySensor");
             return;
         }
         mNotificationPulseTime = SystemClock.elapsedRealtime();
         if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
             runIfNotNull(onPulseSuppressedListener);
-            DozeLog.tracePulseDropped(mContext, "pulseOnNotificationsDisabled");
+            mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
             return;
         }
-        requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
+        requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
                 onPulseSuppressedListener);
-        DozeLog.traceNotificationPulse(mContext);
+        mDozeLog.traceNotificationPulse();
     }
 
     private static void runIfNotNull(Runnable runnable) {
@@ -145,8 +148,7 @@
             final long start = SystemClock.uptimeMillis();
             mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
                 final long end = SystemClock.uptimeMillis();
-                DozeLog.traceProximityResult(
-                        mContext,
+                mDozeLog.traceProximityResult(
                         near == null ? false : near,
                         end - start,
                         reason);
@@ -159,12 +161,12 @@
 
     @VisibleForTesting
     void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
-        boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
-        boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
-        boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
-        boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
-        boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
-        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+        boolean isDoubleTap = pulseReason == DozeEvent.REASON_SENSOR_DOUBLE_TAP;
+        boolean isTap = pulseReason == DozeEvent.REASON_SENSOR_TAP;
+        boolean isPickup = pulseReason == DozeEvent.REASON_SENSOR_PICKUP;
+        boolean isLongPress = pulseReason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS;
+        boolean isWakeDisplay = pulseReason == DozeEvent.REASON_SENSOR_WAKE_UP;
+        boolean isWakeLockScreen = pulseReason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
         boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
 
         if (isWakeDisplay) {
@@ -201,7 +203,7 @@
                     SystemClock.elapsedRealtime() - mNotificationPulseTime;
             final boolean withinVibrationThreshold =
                     timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
-            DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
+            mDozeLog.tracePickupWakeUp(withinVibrationThreshold);
         }
     }
 
@@ -263,7 +265,7 @@
      *              transitions.
      */
     private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
-        DozeLog.traceWakeDisplay(wake);
+        mDozeLog.traceWakeDisplay(wake);
         sWakeDisplaySensorState = wake;
 
         if (wake) {
@@ -277,9 +279,9 @@
                     // Logs AOD open due to sensor wake up.
                     mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
                             .setType(MetricsEvent.TYPE_OPEN)
-                            .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+                            .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
                 }
-            }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+            }, true /* alreadyPerformedProxCheck */, DozeEvent.REASON_SENSOR_WAKE_UP);
         } else {
             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
@@ -288,7 +290,7 @@
                 // Logs AOD close due to sensor wake up.
                 mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
                         .setType(MetricsEvent.TYPE_CLOSE)
-                        .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+                        .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
             }
         }
     }
@@ -346,7 +348,6 @@
 
     private void checkTriggersAtInit() {
         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
-                || mDozeHost.isPowerSaveActive()
                 || mDozeHost.isBlockingDoze()
                 || !mDozeHost.isProvisioned()) {
             mMachine.requestState(DozeMachine.State.FINISH);
@@ -361,14 +362,14 @@
         // When already pulsing we're allowed to show the wallpaper directly without
         // requesting a new pulse.
         if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
-                && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+                && reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
             mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
             return;
         }
 
         if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
             if (mAllowPulseTriggers) {
-                DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
+                mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(),
                         mDozeHost.isPulsingBlocked());
             }
             runIfNotNull(onPulseSuppressedListener);
@@ -379,7 +380,7 @@
         proximityCheckThenCall((result) -> {
             if (result != null && result) {
                 // in pocket, abort pulse
-                DozeLog.tracePulseDropped(mContext, "inPocket");
+                mDozeLog.tracePulseDropped("inPocket");
                 mPulsePending = false;
                 runIfNotNull(onPulseSuppressedListener);
             } else {
@@ -401,7 +402,7 @@
     private void continuePulseRequest(int reason) {
         mPulsePending = false;
         if (mDozeHost.isPulsingBlocked() || !canPulse()) {
-            DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
+            mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(),
                     mDozeHost.isPulsingBlocked());
             return;
         }
@@ -425,7 +426,7 @@
         public void onReceive(Context context, Intent intent) {
             if (PULSE_ACTION.equals(intent.getAction())) {
                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
-                requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
+                requestPulse(DozeEvent.PULSE_REASON_INTENT, false, /* performedProxCheck */
                         null /* onPulseSupressedListener */);
             }
             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
@@ -482,8 +483,8 @@
 
         @Override
         public void onPowerSaveChanged(boolean active) {
-            if (active) {
-                mMachine.requestState(DozeMachine.State.FINISH);
+            if (mDozeHost.isPowerSaveActive()) {
+                mMachine.requestState(DozeMachine.State.DOZE);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1f33af8..2c0ccd21 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -49,6 +49,7 @@
     private final AlarmTimeout mTimeTicker;
     private final boolean mCanAnimateTransition;
     private final DozeParameters mDozeParameters;
+    private final DozeLog mDozeLog;
 
     private boolean mKeyguardShowing;
     private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
@@ -65,7 +66,8 @@
 
     public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
             WakeLock wakeLock, DozeHost host, Handler handler,
-            DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DozeLog dozeLog) {
         mContext = context;
         mMachine = machine;
         mWakeLock = wakeLock;
@@ -75,6 +77,7 @@
         mDozeParameters = params;
         mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
         keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+        mDozeLog = dozeLog;
     }
 
     /**
@@ -83,7 +86,8 @@
      */
     private void updateAnimateScreenOff() {
         if (mCanAnimateTransition) {
-            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+            final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
+                    && !mHost.isPowerSaveActive();
             mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
             mHost.setAnimateScreenOff(controlScreenOff);
         }
@@ -96,7 +100,7 @@
                     public void onPulseStarted() {
                         try {
                             mMachine.requestState(
-                                    reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+                                    reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
                                             ? DozeMachine.State.DOZE_PULSING_BRIGHT
                                             : DozeMachine.State.DOZE_PULSING);
                         } catch (IllegalStateException e) {
@@ -175,7 +179,7 @@
         long delta = roundToNextMinute(time) - System.currentTimeMillis();
         boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
         if (scheduled) {
-            DozeLog.traceTimeTickScheduled(time, time + delta);
+            mDozeLog.traceTimeTickScheduled(time, time + delta);
         }
         mLastTimeTickElapsed = SystemClock.elapsedRealtime();
     }
@@ -192,7 +196,7 @@
         long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed;
         if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) {
             String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick);
-            DozeLog.traceMissedTick(delay);
+            mDozeLog.traceMissedTick(delay);
             Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java
new file mode 100644
index 0000000..92862a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/Event.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
+ * Every event has a time stamp, log level and message.
+ * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
+ */
+public class Event {
+    public static final int UNINITIALIZED = -1;
+
+    @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Level {}
+    public static final int VERBOSE = 2;
+    public static final int DEBUG = 3;
+    public static final int INFO = 4;
+    public static final int WARN = 5;
+    public static final int ERROR = 6;
+
+    private long mTimestamp;
+    private @Level int mLogLevel = DEBUG;
+    protected String mMessage;
+
+    public Event(String message) {
+        mTimestamp = System.currentTimeMillis();
+        mMessage = message;
+    }
+
+    public Event(@Level int logLevel, String message) {
+        mTimestamp = System.currentTimeMillis();
+        mLogLevel = logLevel;
+        mMessage = message;
+    }
+
+    public String getMessage() {
+        return mMessage;
+    }
+
+    public long getTimestamp() {
+        return mTimestamp;
+    }
+
+    public @Level int getLogLevel() {
+        return mLogLevel;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
new file mode 100644
index 0000000..89b7a818
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+/**
+ * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
+ * Every rich event has a time stamp, event type, and log level, with the option to provide the
+ * reason this event was triggered.
+ * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
+ */
+public abstract class RichEvent extends Event {
+    private final int mType;
+    private final String mReason;
+
+    /**
+     * Create a rich event that includes an event type that matches with an index in the array
+     * getEventLabels().
+     */
+    public RichEvent(@Event.Level int logLevel, int type, String reason) {
+        super(logLevel, null);
+        final int numEvents = getEventLabels().length;
+        if (type < 0 || type >= numEvents) {
+            throw new IllegalArgumentException("Unsupported event type. Events only supported"
+                    + " from 0 to " + (numEvents - 1) + ", but given type=" + type);
+        }
+        mType = type;
+        mReason = reason;
+        mMessage = getEventLabels()[mType] + " " + mReason;
+    }
+
+    /**
+     * Returns an array of the event labels.  The index represents the event type and the
+     * corresponding String stored at that index is the user-readable representation of that event.
+     * @return array of user readable events, where the index represents its event type constant
+     */
+    public abstract String[] getEventLabels();
+
+    public int getType() {
+        return mType;
+    }
+
+    public String getReason() {
+        return mReason;
+    }
+
+    /**
+     * Builder to build a RichEvent.
+     * @param <B> Log specific builder that is extending this builder
+     */
+    public abstract static class Builder<B extends Builder<B>> {
+        public static final int UNINITIALIZED = -1;
+
+        private B mBuilder = getBuilder();
+        protected int mType = UNINITIALIZED;
+        protected String mReason;
+        protected @Level int mLogLevel;
+
+        /**
+         * Get the log-specific builder.
+         */
+        public abstract B getBuilder();
+
+        /**
+         * Build the log-specific event.
+         */
+        public abstract RichEvent build();
+
+        /**
+         * Optional - set the log level. Defaults to DEBUG.
+         */
+        public B setLogLevel(@Level int logLevel) {
+            mLogLevel = logLevel;
+            return mBuilder;
+        }
+
+        /**
+         * Required - set the event type.  These events must correspond with the events from
+         * getEventLabels().
+         */
+        public B setType(int type) {
+            mType = type;
+            return mBuilder;
+        }
+
+        /**
+         * Optional - set the reason why this event was triggered.
+         */
+        public B setReason(String reason) {
+            mReason = reason;
+            return mBuilder;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
new file mode 100644
index 0000000..a6e10e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Locale;
+
+/**
+ * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be
+ * printed by the DumpController. This is an alternative to printing directly
+ * to avoid logs being deleted by chatty. The number of logs retained is varied based on
+ * whether the build is {@link Build.IS_DEBUGGABLE}.
+ *
+ * To manually view the logs via adb:
+ *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ *      dependency DumpController <SysuiLogId>
+ */
+public class SysuiLog implements Dumpable {
+    public static final SimpleDateFormat DATE_FORMAT =
+            new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
+
+    private final Object mDataLock = new Object();
+    private final String mId;
+    private final int mMaxLogs;
+    private boolean mEnabled;
+
+    @VisibleForTesting protected ArrayDeque<Event> mTimeline;
+
+    /**
+     * Creates a SysuiLog
+     * To enable or disable logs, set the system property and then restart the device:
+     *      adb shell setprop sysui.log.enabled.<id> true/false && adb reboot
+     * @param dumpController where to register this logger's dumpsys
+     * @param id user-readable tag for this logger
+     * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
+     * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false
+     */
+    public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
+        this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
+                SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED));
+    }
+
+    @VisibleForTesting
+    protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled) {
+        mId = id;
+        mMaxLogs = maxLogs;
+        mEnabled = enabled;
+        mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
+        dumpController.registerDumpable(mId, this);
+    }
+
+    public SysuiLog(DumpController dumpController, String id) {
+        this(dumpController, id, DEFAULT_MAX_DEBUG_LOGS, DEFAULT_MAX_LOGS);
+    }
+
+    /**
+     * Logs an event to the timeline which can be printed by the dumpsys.
+     * May also log to logcat if enabled.
+     * @return true if event was logged, else false
+     */
+    public boolean log(Event event) {
+        if (!mEnabled) {
+            return false;
+        }
+
+        synchronized (mDataLock) {
+            if (mTimeline.size() >= mMaxLogs) {
+                mTimeline.removeFirst();
+            }
+
+            mTimeline.add(event);
+        }
+
+        if (LOG_TO_LOGCAT_ENABLED) {
+            final String strEvent = eventToString(event);
+            switch (event.getLogLevel()) {
+                case Event.VERBOSE:
+                    Log.v(mId, strEvent);
+                    break;
+                case Event.DEBUG:
+                    Log.d(mId, strEvent);
+                    break;
+                case Event.ERROR:
+                    Log.e(mId, strEvent);
+                    break;
+                case Event.INFO:
+                    Log.i(mId, strEvent);
+                    break;
+                case Event.WARN:
+                    Log.w(mId, strEvent);
+                    break;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @return user-readable string of the given event
+     */
+    public String eventToString(Event event) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
+        sb.append(" ");
+        sb.append(event.getMessage());
+        return sb.toString();
+    }
+
+    /**
+     * only call on this method if you have the mDataLock
+     */
+    private void dumpTimelineLocked(PrintWriter pw) {
+        pw.println("\tTimeline:");
+
+        for (Event event : mTimeline) {
+            pw.println("\t" + eventToString(event));
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(mId + ":");
+
+        if (mEnabled) {
+            synchronized (mDataLock) {
+                dumpTimelineLocked(pw);
+            }
+        } else {
+            pw.print(" - Logging disabled.");
+        }
+    }
+
+    private static boolean sDebuggable = Build.IS_DEBUGGABLE;
+    private static final String SYSPROP_ENABLED_PREFIX = "sysui.log.enabled.";
+    private static final boolean LOG_TO_LOGCAT_ENABLED = sDebuggable;
+    private static final boolean DEFAULT_ENABLED = sDebuggable;
+    private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
+    private static final int DEFAULT_MAX_LOGS = 50;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 2542abd..bd3297b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -277,20 +277,7 @@
                     selectPosition(holder.getAdapterPosition(), v);
                 }
             });
-            if (mNeedsFocus) {
-                // Wait for this to get laid out then set its focus.
-                // Ensure that tile gets laid out so we get the callback.
-                holder.mTileView.requestLayout();
-                holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
-                    @Override
-                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                        holder.mTileView.removeOnLayoutChangeListener(this);
-                        holder.mTileView.requestFocus();
-                    }
-                });
-                mNeedsFocus = false;
-            }
+            focusOnHolder(holder);
             return;
         }
 
@@ -330,16 +317,38 @@
                         } else {
                             if (position < mEditIndex && canRemoveTiles()) {
                                 showAccessibilityDialog(position, v);
+                            } else if (position < mEditIndex && !canRemoveTiles()) {
+                                startAccessibleMove(position);
                             } else {
                                 startAccessibleAdd(position);
                             }
                         }
                     }
                 });
+                if (position == mAccessibilityFromIndex) {
+                    focusOnHolder(holder);
+                }
             }
         }
     }
 
+    private void focusOnHolder(Holder holder) {
+        if (mNeedsFocus) {
+            // Wait for this to get laid out then set its focus.
+            // Ensure that tile gets laid out so we get the callback.
+            holder.mTileView.requestLayout();
+            holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    holder.mTileView.removeOnLayoutChangeListener(this);
+                    holder.mTileView.requestFocus();
+                }
+            });
+            mNeedsFocus = false;
+        }
+    }
+
     private boolean canRemoveTiles() {
         return mCurrentSpecs.size() > mMinNumTiles;
     }
@@ -396,6 +405,7 @@
         mAccessibilityFromIndex = position;
         mAccessibilityFromLabel = mTiles.get(position).state.label;
         mAccessibilityAction = ACTION_MOVE;
+        mNeedsFocus = true;
         notifyDataSetChanged();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 4de42cc..22470c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -95,6 +95,9 @@
 
     @Override
     public Intent getLongClickIntent() {
+        if (getState().state == Tile.STATE_UNAVAILABLE) {
+            return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+        }
         return getCellularSettingIntent();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0fc4fe7..a1b4a93 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,25 +21,30 @@
 import android.graphics.Rect;
 import android.provider.Settings;
 
-import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import javax.inject.Inject;
+
 /**
  * A proxy to a Recents implementation.
  */
 public class Recents extends SystemUI implements CommandQueue.Callbacks {
 
-    private RecentsImplementation mImpl;
+    private final RecentsImplementation mImpl;
+
+    @Inject
+    public Recents(RecentsImplementation impl) {
+        mImpl = impl;
+    }
 
     @Override
     public void start() {
         getComponent(CommandQueue.class).addCallback(this);
         putComponent(Recents.class, this);
-        mImpl = createRecentsImplementationFromConfig();
         mImpl.onStart(mContext, this);
     }
 
@@ -139,28 +144,6 @@
                 (Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
     }
 
-    /**
-     * @return The recents implementation from the config.
-     */
-    private RecentsImplementation createRecentsImplementationFromConfig() {
-        final String clsName = mContext.getString(R.string.config_recentsComponent);
-        if (clsName == null || clsName.length() == 0) {
-            throw new RuntimeException("No recents component configured", null);
-        }
-        Class<?> cls = null;
-        try {
-            cls = mContext.getClassLoader().loadClass(clsName);
-        } catch (Throwable t) {
-            throw new RuntimeException("Error loading recents component: " + clsName, t);
-        }
-        try {
-            RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
-            return impl;
-        } catch (Throwable t) {
-            throw new RuntimeException("Error creating recents component: " + clsName, t);
-        }
-    }
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mImpl.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
new file mode 100644
index 0000000..5555285
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger injection module for {@link RecentsImplementation}
+ */
+@Module
+public class RecentsModule {
+    /**
+     * @return The {@link RecentsImplementation} from the config.
+     */
+    @Provides
+    public RecentsImplementation provideRecentsImpl(Context context) {
+        final String clsName = context.getString(R.string.config_recentsComponent);
+        if (clsName == null || clsName.length() == 0) {
+            throw new RuntimeException("No recents component configured", null);
+        }
+        Class<?> cls = null;
+        try {
+            cls = context.getClassLoader().loadClass(clsName);
+        } catch (Throwable t) {
+            throw new RuntimeException("Error loading recents component: " + clsName, t);
+        }
+        try {
+            RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
+            return impl;
+        } catch (Throwable t) {
+            throw new RuntimeException("Error creating recents component: " + clsName, t);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index a70dc7c..e516af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -27,7 +27,6 @@
 import android.view.MotionEvent
 import android.view.VelocityTracker
 import android.view.ViewConfiguration
-import com.android.systemui.Dependency
 
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.Interpolators
@@ -58,7 +57,8 @@
     private val bypassController: KeyguardBypassController,
     private val headsUpManager: HeadsUpManagerPhone,
     private val roundnessManager: NotificationRoundnessManager,
-    private val statusBarStateController: StatusBarStateController
+    private val statusBarStateController: StatusBarStateController,
+    private val falsingManager: FalsingManager
 ) : Gefingerpoken {
     companion object {
         private val RUBBERBAND_FACTOR_STATIC = 0.25f
@@ -99,7 +99,6 @@
     private val mTemp2 = IntArray(2)
     private var mDraggedFarEnough: Boolean = false
     private var mStartingChild: ExpandableView? = null
-    private val mFalsingManager: FalsingManager
     private var mPulsing: Boolean = false
     var isWakingToShadeLocked: Boolean = false
         private set
@@ -109,7 +108,7 @@
     private var velocityTracker: VelocityTracker? = null
 
     private val isFalseTouch: Boolean
-        get() = mFalsingManager.isFalseTouch
+        get() = falsingManager.isFalseTouch
     var qsExpanded: Boolean = false
     var pulseExpandAbortListener: Runnable? = null
     var bouncerShowing: Boolean = false
@@ -118,7 +117,6 @@
         mMinDragDistance = context.resources.getDimensionPixelSize(
                 R.dimen.keyguard_drag_down_min_distance)
         mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat()
-        mFalsingManager = Dependency.get(FalsingManager::class.java)
         mPowerManager = context.getSystemService(PowerManager::class.java)
     }
 
@@ -151,7 +149,7 @@
             MotionEvent.ACTION_MOVE -> {
                 val h = y - mInitialTouchY
                 if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
-                    mFalsingManager.onStartExpandingFromPulse()
+                    falsingManager.onStartExpandingFromPulse()
                     isExpanding = true
                     captureStartingChild(mInitialTouchX, mInitialTouchY)
                     mInitialTouchY = y
@@ -192,7 +190,7 @@
                 velocityTracker!!.computeCurrentVelocity(1000 /* units */)
                 val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
                         statusBarStateController.state != StatusBarState.SHADE
-                if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
+                if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
                     finishExpansion()
                 } else {
                     cancelExpansion()
@@ -297,7 +295,7 @@
 
     private fun cancelExpansion() {
         isExpanding = false
-        mFalsingManager.onExpansionFromPulseStopped()
+        falsingManager.onExpansionFromPulseStopped()
         if (mStartingChild != null) {
             reset(mStartingChild!!)
             mStartingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 9362d2d..eadec6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import javax.inject.Inject;
@@ -63,6 +64,7 @@
     private final Context mContext;
     private final PowerManager mPowerManager;
     private final IDreamManager mDreamManager;
+    private final BatteryController mBatteryController;
 
     private NotificationPresenter mPresenter;
     private HeadsUpManager mHeadsUpManager;
@@ -75,13 +77,14 @@
 
     @Inject
     public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
-            StatusBarStateController stateController) {
+            StatusBarStateController stateController, BatteryController batteryController) {
         this(context,
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE),
                 IDreamManager.Stub.asInterface(
                         ServiceManager.checkService(DreamService.DREAM_SERVICE)),
                 new AmbientDisplayConfiguration(context),
                 filter,
+                batteryController,
                 stateController);
     }
 
@@ -92,10 +95,12 @@
             IDreamManager dreamManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             NotificationFilter notificationFilter,
+            BatteryController batteryController,
             StatusBarStateController statusBarStateController) {
         mContext = context;
         mPowerManager = powerManager;
         mDreamManager = dreamManager;
+        mBatteryController = batteryController;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
         mNotificationFilter = notificationFilter;
         mStatusBarStateController = statusBarStateController;
@@ -293,6 +298,13 @@
             return false;
         }
 
+        if (mBatteryController.isAodPowerSave()) {
+            if (DEBUG_HEADS_UP) {
+                Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey());
+            }
+            return false;
+        }
+
         if (!canAlertCommon(entry)) {
             if (DEBUG_HEADS_UP) {
                 Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
new file mode 100644
index 0000000..2396d28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging;
+
+import android.annotation.IntDef;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.log.RichEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event related to notifications. {@link NotifLog} stores and prints these events for debugging
+ * and triaging purposes.
+ */
+public class NotifEvent extends RichEvent {
+    public static final int TOTAL_EVENT_TYPES = 8;
+    private StatusBarNotification mSbn;
+    private Ranking mRanking;
+
+    /**
+     * Creates a NotifEvent with an event type that matches with an index in the array
+     * getSupportedEvents() and {@link EventType}.
+     *
+     * The status bar notification and ranking objects are stored as shallow copies of the current
+     * state of the event when this event occurred.
+     */
+    public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn,
+            Ranking ranking) {
+        super(logLevel, type, reason);
+        mSbn = sbn.clone();
+        mRanking = new Ranking();
+        mRanking.populate(ranking);
+        mMessage += getExtraInfo();
+    }
+
+    private String getExtraInfo() {
+        StringBuilder extraInfo = new StringBuilder();
+
+        if (mSbn != null) {
+            extraInfo.append(" Sbn=");
+            extraInfo.append(mSbn);
+        }
+
+        if (mRanking != null) {
+            extraInfo.append(" Ranking=");
+            extraInfo.append(mRanking);
+        }
+
+        return extraInfo.toString();
+    }
+
+    /**
+     * Event labels for NotifEvents
+     * Index corresponds to the {@link EventType}
+     */
+    @Override
+    public String[] getEventLabels() {
+        final String[] events = new String[]{
+                "NotifAdded",
+                "NotifRemoved",
+                "NotifUpdated",
+                "HeadsUpStarted",
+                "HeadsUpEnded",
+                "Filter",
+                "Sort",
+                "NotifVisibilityChanged",
+        };
+
+        if (events.length != TOTAL_EVENT_TYPES) {
+            throw new IllegalStateException("NotifEvents events.length should match "
+                    + TOTAL_EVENT_TYPES
+                    + " events.length=" + events.length
+                    + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
+        }
+        return events;
+    }
+
+    /**
+     * @return a copy of the status bar notification that changed with this event
+     */
+    public StatusBarNotification getSbn() {
+        return mSbn;
+    }
+
+    /**
+     * Builds a NotifEvent.
+     */
+    public static class NotifEventBuilder extends RichEvent.Builder<NotifEventBuilder> {
+        private StatusBarNotification mSbn;
+        private Ranking mRanking;
+
+        @Override
+        public NotifEventBuilder getBuilder() {
+            return this;
+        }
+
+        /**
+         * Stores the status bar notification object. A shallow copy is stored in the NotifEvent's
+         * constructor.
+         */
+        public NotifEventBuilder setSbn(StatusBarNotification sbn) {
+            mSbn = sbn;
+            return this;
+        }
+
+        /**
+         * Stores the ranking object. A shallow copy is stored in the NotifEvent's
+         * constructor.
+         */
+        public NotifEventBuilder setRanking(Ranking ranking) {
+            mRanking = ranking;
+            return this;
+        }
+
+        @Override
+        public RichEvent build() {
+            return new NotifEvent(mLogLevel, mType, mReason, mSbn, mRanking);
+        }
+    }
+
+    @IntDef({NOTIF_ADDED, NOTIF_REMOVED, NOTIF_UPDATED, HEADS_UP_STARTED, HEADS_UP_ENDED, FILTER,
+            SORT, NOTIF_VISIBILITY_CHANGED})
+    /**
+     * Types of NotifEvents
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EventType {}
+    public static final int NOTIF_ADDED = 0;
+    public static final int NOTIF_REMOVED = 1;
+    public static final int NOTIF_UPDATED = 2;
+    public static final int HEADS_UP_STARTED = 3;
+    public static final int HEADS_UP_ENDED = 4;
+    public static final int FILTER = 5;
+    public static final int SORT = 6;
+    public static final int NOTIF_VISIBILITY_CHANGED = 7;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
new file mode 100644
index 0000000..d42cd82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.log.SysuiLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in
+ * bugreports or on demand:
+ *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ *      dependency DumpController NotifLog
+ */
+@Singleton
+public class NotifLog extends SysuiLog {
+    private static final String TAG = "NotifLog";
+    private static final int MAX_DOZE_DEBUG_LOGS = 400;
+    private static final int MAX_DOZE_LOGS = 50;
+
+    @Inject
+    public NotifLog(DumpController dumpController) {
+        super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a notification, ranking and message
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
+            Ranking ranking, String msg) {
+        return log(new NotifEvent.NotifEventBuilder()
+                .setType(eventType)
+                .setSbn(sbn)
+                .setRanking(ranking)
+                .setReason(msg)
+                .build());
+    }
+
+    /**
+     * Logs a {@link NotifEvent}
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType) {
+        return log(eventType, null, null, null);
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a message
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, String msg) {
+        return log(eventType, null, null, msg);
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a notification
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn) {
+        return log(eventType, sbn, null, "");
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a ranking
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, Ranking ranking) {
+        return log(eventType, null, ranking, "");
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a notification and ranking
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
+            Ranking ranking) {
+        return log(eventType, sbn, ranking, "");
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a notification entry
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
+        return log(eventType, entry.sbn(), entry.ranking(), "");
+    }
+
+    /**
+     * Logs a {@link NotifEvent} with a notification entry
+     * @return true if successfully logged, else false
+     */
+    public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry,
+            String msg) {
+        return log(eventType, entry.sbn(), entry.ranking(), msg);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8d73251..a817f54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -180,6 +180,10 @@
         initDimens();
     }
 
+    public FalsingManager getFalsingManager() {
+        return mFalsingManager;
+    }
+
     private void updateColors() {
         mNormalColor = mContext.getColor(R.color.notification_material_background_color);
         mTintedRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0f6ce21..9f4b026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -74,7 +74,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
@@ -221,7 +220,6 @@
     private ViewStub mGutsStub;
     private boolean mIsSystemChildExpanded;
     private boolean mIsPinned;
-    private FalsingManager mFalsingManager;
     private boolean mExpandAnimationRunning;
     private AboveShelfChangedListener mAboveShelfChangedListener;
     private HeadsUpManager mHeadsUpManager;
@@ -1636,7 +1634,6 @@
 
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mFalsingManager = Dependency.get(FalsingManager.class);  // TODO: inject into a controller.
         mNotificationInflater = new NotificationContentInflater(this);
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
@@ -2208,7 +2205,7 @@
      * @param allowChildExpansion whether a call to this method allows expanding children
      */
     public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
-        mFalsingManager.setNotificationExpanded();
+        getFalsingManager().setNotificationExpanded();
         if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
                 && !mChildrenContainer.showingAsLowPriority()) {
             final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 60e381a..fe3c04e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -22,11 +22,14 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
+import com.android.systemui.doze.DozeEvent;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 
+import javax.inject.Inject;
+
 /**
  * Controller which handles all the doze animations of the scrims.
  */
@@ -34,6 +37,7 @@
     private static final String TAG = "DozeScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private final DozeLog mDozeLog;
     private final DozeParameters mDozeParameters;
     private final Handler mHandler = new Handler();
 
@@ -47,7 +51,7 @@
         public void onDisplayBlanked() {
             if (DEBUG) {
                 Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
-                        + DozeLog.reasonToString(mPulseReason));
+                        + DozeEvent.reasonToString(mPulseReason));
             }
             if (!mDozing) {
                 return;
@@ -68,8 +72,8 @@
             // Notifications should time out on their own.  Pulses due to notifications should
             // instead be managed externally based off the notification's lifetime.
             // Dock also controls the time out by self.
-            if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION
-                    && mPulseReason != DozeLog.PULSE_REASON_DOCKING) {
+            if (mPulseReason != DozeEvent.PULSE_REASON_NOTIFICATION
+                    && mPulseReason != DozeEvent.PULSE_REASON_DOCKING) {
                 mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
                 mHandler.postDelayed(mPulseOutExtended,
                         mDozeParameters.getPulseVisibleDurationExtended());
@@ -90,14 +94,16 @@
          */
         @Override
         public boolean shouldTimeoutWallpaper() {
-            return mPulseReason == DozeLog.PULSE_REASON_DOCKING;
+            return mPulseReason == DozeEvent.PULSE_REASON_DOCKING;
         }
     };
 
-    public DozeScrimController(DozeParameters dozeParameters) {
+    @Inject
+    public DozeScrimController(DozeParameters dozeParameters, DozeLog dozeLog) {
         mDozeParameters = dozeParameters;
         //Never expected to be destroyed
         Dependency.get(StatusBarStateController.class).addCallback(this);
+        mDozeLog = dozeLog;
     }
 
     @VisibleForTesting
@@ -168,14 +174,14 @@
     }
 
     private void pulseStarted() {
-        DozeLog.tracePulseStart(mPulseReason);
+        mDozeLog.tracePulseStart(mPulseReason);
         if (mPulseCallback != null) {
             mPulseCallback.onPulseStarted();
         }
     }
 
     private void pulseFinished() {
-        DozeLog.tracePulseFinish();
+        mDozeLog.tracePulseFinish();
         if (mPulseCallback != null) {
             mPulseCallback.onPulseFinished();
             mPulseCallback = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
index 7dcc2fc..53601ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
 import android.widget.FrameLayout
 import com.android.systemui.plugins.NPVPlugin
 import com.android.systemui.plugins.PluginListener
@@ -36,6 +37,7 @@
 
     private var plugin: NPVPlugin? = null
     private var animator = createAnimator()
+    private var yOffset = 0f
 
     private fun createAnimator() = TouchAnimator.Builder()
             .addFloat(parent, "alpha", 1f, 0f)
@@ -76,7 +78,7 @@
     }
 
     fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) {
-        parent.setTranslationY(expansion * heightDiff + headerTranslation)
+        parent.setTranslationY(expansion * heightDiff + headerTranslation + yOffset)
         if (!expansion.isNaN()) animator.setPosition(expansion)
     }
 
@@ -88,5 +90,13 @@
         animator = createAnimator()
     }
 
-    fun getHeight() = if (plugin != null) parent.height else 0
+    fun getHeight() =
+        if (plugin != null) {
+            parent.height + (parent.getLayoutParams() as MarginLayoutParams).topMargin
+        } else 0
+
+    fun setYOffset(y: Float) {
+        yOffset = y
+        parent.setTranslationY(yOffset)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 86da10a..cee1d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -68,6 +68,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.doze.DozeLog;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.FalsingManager;
@@ -459,8 +460,9 @@
             DynamicPrivacyController dynamicPrivacyController,
             KeyguardBypassController bypassController,
             FalsingManager falsingManager,
-            PluginManager pluginManager) {
-        super(context, attrs);
+            PluginManager pluginManager,
+            DozeLog dozeLog) {
+        super(context, attrs, falsingManager, dozeLog);
         setWillNotDraw(!DEBUG);
         mInjectionInflationController = injectionInflationController;
         mFalsingManager = falsingManager;
@@ -652,8 +654,7 @@
             mNotificationStackScroller.setLayoutParams(lp);
         }
         int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
-        int topMargin =
-                res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
+        int topMargin = sideMargin;
         lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
         if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
                 || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
@@ -796,6 +797,7 @@
         int oldMaxHeight = mQsMaxExpansionHeight;
         if (mQs != null) {
             mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+            mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
             mQsMinExpansionHeight += mNPVPluginManager.getHeight();
             mQsMaxExpansionHeight = mQs.getDesiredHeight();
             mNotificationStackScroller.setMaxTopPadding(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index ffaf3d5..432d636 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -111,6 +111,7 @@
     private FlingAnimationUtils mFlingAnimationUtilsClosing;
     private FlingAnimationUtils mFlingAnimationUtilsDismissing;
     private final FalsingManager mFalsingManager;
+    private final DozeLog mDozeLog;
     private final VibratorHelper mVibratorHelper;
 
     /**
@@ -204,7 +205,8 @@
         mJustPeeked = true;
     }
 
-    public PanelView(Context context, AttributeSet attrs) {
+    public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager,
+            DozeLog dozeLog) {
         super(context, attrs);
         mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */,
                 0.6f /* speedUpFactor */);
@@ -214,7 +216,8 @@
                 0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
                 0.84f /* y2 */);
         mBounceInterpolator = new BounceInterpolator();
-        mFalsingManager = Dependency.get(FalsingManager.class);  // TODO: inject into a controller.
+        mFalsingManager = falsingManager;
+        mDozeLog = dozeLog;
         mNotificationsDragEnabled =
                 getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
         mVibratorHelper = Dependency.get(VibratorHelper.class);
@@ -477,7 +480,7 @@
             boolean expand = flingExpands(vel, vectorVel, x, y)
                     || event.getActionMasked() == MotionEvent.ACTION_CANCEL
                     || forceCancel;
-            DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
+            mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
                     mStatusBar.isFalsingThresholdNeeded(),
                     mStatusBar.isWakeUpComingFromTouch());
                     // Log collapse gesture if on lock screen.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index bd9ce3a..3817197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -754,6 +754,16 @@
     }
 
     private void onFinished(Callback callback) {
+        if (!hasReachedFinalState(mScrimBehind)
+            || !hasReachedFinalState(mScrimInFront)
+            || !hasReachedFinalState(mScrimForBubble)) {
+            if (callback != null && callback != mCallback) {
+                // Since we only notify the callback that we're finished once everything has
+                // finished, we need to make sure that any changing callbacks are also invoked
+                callback.onFinished();
+            }
+            return;
+        }
         if (mWakeLockHeld) {
             mWakeLock.release(TAG);
             mWakeLockHeld = false;
@@ -773,9 +783,17 @@
             mInFrontTint = Color.TRANSPARENT;
             mBehindTint = Color.TRANSPARENT;
             mBubbleTint = Color.TRANSPARENT;
+            updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
+            updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
+            updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
         }
     }
 
+    private boolean hasReachedFinalState(ScrimView scrim) {
+        return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim)
+                && scrim.getTint() == getCurrentScrimTint(scrim);
+    }
+
     private boolean isAnimating(View scrim) {
         return scrim.getTag(TAG_KEY_ANIM) != null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7bab7f1..5fc2d9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -152,6 +152,7 @@
 import com.android.systemui.charging.WirelessChargingAnimation;
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.doze.DozeEvent;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.doze.DozeReceiver;
@@ -370,6 +371,8 @@
 
     protected StatusBarIconController mIconController;
     @Inject
+    DozeLog mDozeLog;
+    @Inject
     InjectionInflationController mInjectionInflater;
     @Inject
     PulseExpansionHandler mPulseExpansionHandler;
@@ -393,6 +396,8 @@
     boolean mAllowNotificationLongPress;
     @Inject
     protected NotifPipelineInitializer mNotifPipelineInitializer;
+    @Inject
+    protected FalsingManager mFalsingManager;
 
     @VisibleForTesting
     BroadcastDispatcher mBroadcastDispatcher;
@@ -584,7 +589,6 @@
         }
     };
     private boolean mNoAnimationOnNextBarModeChange;
-    protected FalsingManager mFalsingManager;
     private final SysuiStatusBarStateController mStatusBarStateController =
             (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
 
@@ -716,7 +720,6 @@
         mRecents = getComponent(Recents.class);
 
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mFalsingManager = Dependency.get(FalsingManager.class);
 
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
@@ -968,7 +971,8 @@
                 mKeyguardStateController);
         mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
-        mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
+        mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context),
+                mDozeLog);
 
         BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
         mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
@@ -2377,7 +2381,7 @@
         final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light;
         pw.println("    light wallpaper theme: " + lightWpTheme);
 
-        DozeLog.dump(pw);
+        mDozeLog.dump(pw);
 
         if (mBiometricUnlockController != null) {
             mBiometricUnlockController.dump(pw);
@@ -2444,7 +2448,7 @@
             mKeyguardUpdateMonitor.dump(fd, pw, args);
         }
 
-        Dependency.get(FalsingManager.class).dump(pw);
+        mFalsingManager.dump(pw);
         FalsingLog.dump(pw);
 
         pw.println("SharedPreferences:");
@@ -4003,7 +4007,7 @@
         public void startDozing() {
             if (!mDozingRequested) {
                 mDozingRequested = true;
-                DozeLog.traceDozing(mContext, mDozing);
+                mDozeLog.traceDozing(mDozing);
                 updateDozing();
                 updateIsKeyguard();
             }
@@ -4011,22 +4015,22 @@
 
         @Override
         public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
-            if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
+            if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) {
                 mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
                         "com.android.systemui:LONG_PRESS");
                 startAssist(new Bundle());
                 return;
             }
 
-            if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+            if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
                 mScrimController.setWakeLockScreenSensorActive(true);
             }
 
-            if (reason == DozeLog.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
+            if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
                 mStatusBarWindow.suppressWakeUpGesture(true);
             }
 
-            boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+            boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
                             && mWakeLockScreenPerformsAuth;
             // Set the state to pulsing, so ScrimController will know what to do once we ask it to
             // execute the transition. The pulse callback will then be invoked when the scrims
@@ -4077,7 +4081,7 @@
         public void stopDozing() {
             if (mDozingRequested) {
                 mDozingRequested = false;
-                DozeLog.traceDozing(mContext, mDozing);
+                mDozeLog.traceDozing(mDozing);
                 updateDozing();
             }
         }
@@ -4085,7 +4089,7 @@
         @Override
         public void onIgnoreTouchWhilePulsing(boolean ignore) {
             if (ignore != mIgnoreTouchWhilePulsing) {
-                DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
+                mDozeLog.tracePulseTouchDisabledByProx(ignore);
             }
             mIgnoreTouchWhilePulsing = ignore;
             if (isDozing() && ignore) {
@@ -4129,7 +4133,7 @@
 
         @Override
         public void extendPulse(int reason) {
-            if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+            if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
                 mScrimController.setWakeLockScreenSensorActive(true);
             }
             if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 111cdd2..738d076 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -45,9 +45,7 @@
     /**
      * Returns {@code true} if AOD was disabled by power saving policies.
      */
-    default boolean isAodPowerSave() {
-        return isPowerSave();
-    }
+    boolean isAodPowerSave();
 
     /**
      * A listener that will be notified whenever a change in battery level or power save mode has
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index edea92f..2c70fb4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -621,7 +621,9 @@
                 .PRIORITY_CATEGORY_MEDIA) == 0;
         boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy
                 .PRIORITY_CATEGORY_SYSTEM) == 0;
-        boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(policy);
+        // ringer controls notifications, ringer and system sounds, so only disallow ringer changes
+        // if all relevant (notifications + ringer + system) sounds are not allowed to bypass DND
+        boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy);
         if (mState.disallowAlarms == disallowAlarms
                 && mState.disallowMedia == disallowMedia
                 && mState.disallowRinger == disallowRinger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 448c80e..2798c6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -74,6 +74,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -174,7 +175,8 @@
         TestableNotificationInterruptionStateProvider interruptionStateProvider =
                 new TestableNotificationInterruptionStateProvider(mContext,
                         mock(NotificationFilter.class),
-                        mock(StatusBarStateController.class));
+                        mock(StatusBarStateController.class),
+                        mock(BatteryController.class));
         interruptionStateProvider.setUpWithPresenter(
                 mock(NotificationPresenter.class),
                 mock(HeadsUpManager.class),
@@ -660,8 +662,9 @@
             NotificationInterruptionStateProvider {
 
         TestableNotificationInterruptionStateProvider(Context context,
-                NotificationFilter filter, StatusBarStateController controller) {
-            super(context, filter, controller);
+                NotificationFilter filter, StatusBarStateController controller,
+                BatteryController batteryController) {
+            super(context, filter, controller, batteryController);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index af2de1b..1ce0172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -43,7 +43,6 @@
 import com.android.systemui.doze.DozeMachine.State;
 
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -58,12 +57,6 @@
     private Instrumentation mInstrumentation;
     private DockManagerFake mDockManagerFake;
 
-    @BeforeClass
-    public static void setupSuite() {
-        // We can't use KeyguardUpdateMonitor from tests.
-        DozeLog.setRegisterKeyguardCallback(false);
-    }
-
     @Before
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -95,7 +88,7 @@
 
         mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
 
-        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+        verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
     }
 
     @Test
@@ -105,14 +98,14 @@
 
         mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
 
-        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+        verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
     }
 
     @Test
     public void testOnEvent_dockedHideWhenPulsing_requestPulseOut() {
         mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
-        when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+        when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
 
         mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
 
@@ -123,7 +116,7 @@
     public void testOnEvent_undockedWhenPulsing_requestPulseOut() {
         mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
-        when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+        when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
 
         mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
 
@@ -161,7 +154,7 @@
 
         TestableLooper.get(this).processAllMessages();
 
-        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+        verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
     }
 
     @Test
@@ -174,7 +167,7 @@
 
         TestableLooper.get(this).processAllMessages();
 
-        verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+        verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
     }
 
     @Test
@@ -186,7 +179,7 @@
 
         mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
 
-        verify(mMachine, never()).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+        verify(mMachine, never()).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
     }
 
     @Test
@@ -205,7 +198,7 @@
     public void testTransitionToPulsing_whenDockedHide_requestPulseOut() {
         mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
-        when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+        when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
         mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
 
         mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, State.DOZE_PULSING);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 1e18e51..bbd2ab1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -46,6 +46,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.Before;
@@ -63,6 +64,8 @@
 
     @Mock
     private WakefulnessLifecycle mWakefulnessLifecycle;
+    @Mock
+    private DozeLog mDozeLog;
     private DozeServiceFake mServiceFake;
     private WakeLockFake mWakeLockFake;
     private AmbientDisplayConfiguration mConfigMock;
@@ -76,8 +79,8 @@
         mConfigMock = mock(AmbientDisplayConfiguration.class);
         mPartMock = mock(DozeMachine.Part.class);
 
-        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle);
-
+        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
+                mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog);
         mMachine.setParts(new DozeMachine.Part[]{mPartMock});
     }
 
@@ -112,7 +115,7 @@
     public void testPulseDone_goesToDoze() {
         when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
         mMachine.requestState(INITIALIZED);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSING);
 
         mMachine.requestState(DOZE_PULSE_DONE);
@@ -125,7 +128,7 @@
     public void testPulseDone_goesToAoD() {
         when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
         mMachine.requestState(INITIALIZED);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSING);
 
         mMachine.requestState(DOZE_PULSE_DONE);
@@ -169,7 +172,7 @@
     public void testWakeLock_heldInPulseStates() {
         mMachine.requestState(INITIALIZED);
 
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         assertTrue(mWakeLockFake.isHeld());
 
         mMachine.requestState(DOZE_PULSING);
@@ -192,7 +195,7 @@
         mMachine.requestState(INITIALIZED);
 
         mMachine.requestState(DOZE);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSING);
         mMachine.requestState(DOZE_PULSE_DONE);
 
@@ -204,9 +207,9 @@
         mMachine.requestState(INITIALIZED);
 
         mMachine.requestState(DOZE);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSING);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSE_DONE);
     }
 
@@ -215,7 +218,7 @@
         mMachine.requestState(INITIALIZED);
 
         mMachine.requestState(DOZE);
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSE_DONE);
     }
 
@@ -228,7 +231,7 @@
             return null;
         }).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE));
 
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
 
         assertEquals(DOZE_PULSING, mMachine.getState());
     }
@@ -237,9 +240,9 @@
     public void testPulseReason_getMatchesRequest() {
         mMachine.requestState(INITIALIZED);
         mMachine.requestState(DOZE);
-        mMachine.requestPulse(DozeLog.REASON_SENSOR_DOUBLE_TAP);
+        mMachine.requestPulse(DozeEvent.REASON_SENSOR_DOUBLE_TAP);
 
-        assertEquals(DozeLog.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
+        assertEquals(DozeEvent.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
     }
 
     @Test
@@ -251,7 +254,7 @@
             if (newState == DOZE_REQUEST_PULSE
                     || newState == DOZE_PULSING
                     || newState == DOZE_PULSE_DONE) {
-                assertEquals(DozeLog.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason());
+                assertEquals(DozeEvent.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason());
             } else {
                 assertTrue("unexpected state " + newState,
                         newState == DOZE || newState == DOZE_AOD);
@@ -259,7 +262,7 @@
             return null;
         }).when(mPartMock).transitionTo(any(), any());
 
-        mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+        mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
         mMachine.requestState(DOZE_PULSING);
         mMachine.requestState(DOZE_PULSE_DONE);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index ddd1685..f2665ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -78,6 +78,8 @@
     private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
     @Mock
     private TriggerSensor mTriggerSensor;
+    @Mock
+    private DozeLog mDozeLog;
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
     private DozeSensors mDozeSensors;
@@ -101,14 +103,14 @@
 
         mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
         mTestableLooper.processAllMessages();
-        verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+        verify(mCallback).onSensorPulse(eq(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
                 anyFloat(), anyFloat(), eq(null));
 
         mDozeSensors.requestTemporaryDisable();
         reset(mCallback);
         mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
         mTestableLooper.processAllMessages();
-        verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+        verify(mCallback, never()).onSensorPulse(eq(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
                 anyFloat(), anyFloat(), eq(null));
     }
 
@@ -146,7 +148,7 @@
         TestableDozeSensors() {
             super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
                     mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback,
-                    mAlwaysOnDisplayPolicy);
+                    mAlwaysOnDisplayPolicy, mDozeLog);
             for (TriggerSensor sensor : mSensors) {
                 if (sensor instanceof PluginSensor
                         && ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index b0e3969..e5ae6d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -48,7 +48,6 @@
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -64,12 +63,6 @@
     private DockManager mDockManagerFake;
     private FakeProximitySensor mProximitySensor;
 
-    @BeforeClass
-    public static void setupSuite() {
-        // We can't use KeyguardUpdateMonitor from tests.
-        DozeLog.setRegisterKeyguardCallback(false);
-    }
-
     @Before
     public void setUp() throws Exception {
         mMachine = mock(DozeMachine.class);
@@ -87,7 +80,7 @@
 
         mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
                 asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
-                mDockManagerFake, mProximitySensor);
+                mDockManagerFake, mProximitySensor, mock(DozeLog.class));
         waitForSensorManager();
     }
 
@@ -148,9 +141,10 @@
     @Test
     public void testProximitySensorNotAvailablel() {
         mProximitySensor.setSensorAvailable(false);
-        mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null);
-        mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100, new float[]{1});
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null);
+        mTriggers.onSensor(DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null);
+        mTriggers.onSensor(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100,
+                new float[]{1});
+        mTriggers.onSensor(DozeEvent.REASON_SENSOR_TAP, 100, 100, null);
     }
 
     private void waitForSensorManager() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 25231bc..c5bddc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -65,6 +65,8 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private DozeHost mHost;
+    @Mock
+    private DozeLog mDozeLog;
     private WakeLockFake mWakeLock;
     private Handler mHandler;
     private HandlerThread mHandlerThread;
@@ -80,7 +82,7 @@
         mHandler = mHandlerThread.getThreadHandler();
 
         mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
-                mDozeParameters, mKeyguardUpdateMonitor);
+                mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
     }
 
     @After
@@ -135,7 +137,7 @@
         reset(mHost);
         when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
         mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
-                mDozeParameters, mKeyguardUpdateMonitor);
+                mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
 
         // Never animate if display doesn't support it.
         mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
new file mode 100644
index 0000000..2f90641
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class RichEventTest extends SysuiTestCase {
+
+    private static final int TOTAL_EVENT_TYPES = 1;
+
+    @Test
+    public void testCreateRichEvent_invalidType() {
+        try {
+            // indexing for events starts at 0, so TOTAL_EVENT_TYPES is an invalid type
+            new TestableRichEvent(Event.DEBUG, TOTAL_EVENT_TYPES, "msg");
+        } catch (IllegalArgumentException e) {
+            // expected
+            return;
+        }
+
+        Assert.fail("Expected an invalidArgumentException since the event type was invalid.");
+    }
+
+    @Test
+    public void testCreateRichEvent() {
+        final int eventType = 0;
+        RichEvent e = new TestableRichEvent(Event.DEBUG, eventType, "msg");
+        assertEquals(e.getType(), eventType);
+    }
+
+    class TestableRichEvent extends RichEvent {
+        TestableRichEvent(int logLevel, int type, String reason) {
+            super(logLevel, type, reason);
+        }
+
+        @Override
+        public String[] getEventLabels() {
+            return new String[]{"ACTION_NAME"};
+        }
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
new file mode 100644
index 0000000..378bba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SysuiLogTest extends SysuiTestCase {
+    private static final String TEST_ID = "TestLogger";
+    private static final int MAX_LOGS = 5;
+
+    @Mock
+    private DumpController mDumpController;
+    private SysuiLog mSysuiLog;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testLogDisabled_noLogsWritten() {
+        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
+        assertEquals(mSysuiLog.mTimeline, null);
+
+        mSysuiLog.log(new Event("msg"));
+        assertEquals(mSysuiLog.mTimeline, null);
+    }
+
+    @Test
+    public void testLogEnabled_logWritten() {
+        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+        assertEquals(mSysuiLog.mTimeline.size(), 0);
+
+        mSysuiLog.log(new Event("msg"));
+        assertEquals(mSysuiLog.mTimeline.size(), 1);
+    }
+
+    @Test
+    public void testMaxLogs() {
+        mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+        assertEquals(mSysuiLog.mTimeline.size(), 0);
+
+        final String msg = "msg";
+        for (int i = 0; i < MAX_LOGS + 1; i++) {
+            mSysuiLog.log(new Event(msg + i));
+        }
+
+        assertEquals(mSysuiLog.mTimeline.size(), MAX_LOGS);
+
+        // check the first message (msg0) is deleted:
+        assertEquals(mSysuiLog.mTimeline.getFirst().getMessage(), msg + "1");
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index 350ab5a..28a7e40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
@@ -84,6 +85,8 @@
     HeadsUpManager mHeadsUpManager;
     @Mock
     NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
+    @Mock
+    BatteryController mBatteryController;
 
     private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
 
@@ -97,7 +100,8 @@
                         mDreamManager,
                         mAmbientDisplayConfiguration,
                         mNotificationFilter,
-                        mStatusBarStateController);
+                        mStatusBarStateController,
+                        mBatteryController);
 
         mNotifInterruptionStateProvider.setUpWithPresenter(
                 mPresenter,
@@ -590,17 +594,17 @@
     /**
      * Testable class overriding constructor.
      */
-    public class TestableNotificationInterruptionStateProvider extends
+    public static class TestableNotificationInterruptionStateProvider extends
             NotificationInterruptionStateProvider {
 
         TestableNotificationInterruptionStateProvider(Context context,
                 PowerManager powerManager, IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter notificationFilter,
-                StatusBarStateController statusBarStateController) {
+                StatusBarStateController statusBarStateController,
+                BatteryController batteryController) {
             super(context, powerManager, dreamManager, ambientDisplayConfiguration,
-                    notificationFilter,
-                    statusBarStateController);
+                    notificationFilter, batteryController, statusBarStateController);
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 20c739f..1ce336e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -27,6 +27,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -41,12 +42,14 @@
 
     @Mock
     private DozeParameters mDozeParameters;
+    @Mock
+    private DozeLog mDozeLog;
     private DozeScrimController mDozeScrimController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mDozeScrimController = new DozeScrimController(mDozeParameters);
+        mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog);
         mDozeScrimController.setDozing(true);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 219aef1..f1da4e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -40,6 +40,8 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -129,9 +131,13 @@
                         mock(HeadsUpManagerPhone.class),
                         new StatusBarStateControllerImpl(),
                         mKeyguardBypassController);
-        PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
+        PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
+                mContext,
+                coordinator,
                 mKeyguardBypassController, mHeadsUpManager,
-                mock(NotificationRoundnessManager.class), mStatusBarStateController);
+                mock(NotificationRoundnessManager.class),
+                mStatusBarStateController,
+                new FalsingManagerFake());
         mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
                 mKeyguardBypassController);
         mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
@@ -216,7 +222,7 @@
                             SystemUIFactory.getInstance().getRootComponent()),
                     coordinator, expansionHandler, mock(DynamicPrivacyController.class),
                     bypassController,
-                    mFalsingManager, mock(PluginManager.class));
+                    mFalsingManager, mock(PluginManager.class), mock(DozeLog.class));
             mNotificationStackScroller = mNotificationStackScrollLayout;
             mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
             mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index f1aaf3d..4140b0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -433,6 +433,7 @@
                 TRANSPARENT /* behind */,
                 TRANSPARENT /* bubble */);
 
+        // Make sure at the very end of the animation, we're reset to transparent
         assertScrimTint(false /* front */,
                 false /* behind */,
                 false  /* bubble */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b75cb8c..32b0f76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -80,8 +80,8 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.doze.DozeEvent;
 import com.android.systemui.doze.DozeHost;
-import com.android.systemui.doze.DozeLog;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -113,6 +113,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -159,6 +160,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+    @Mock private BatteryController mBatteryController;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private StatusBarNotificationPresenter mNotificationPresenter;
     @Mock
@@ -214,7 +216,7 @@
         mNotificationInterruptionStateProvider =
                 new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
                         mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
-                        mStatusBarStateController);
+                        mStatusBarStateController, mBatteryController);
         mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
                 mNotificationInterruptionStateProvider);
         mDependency.injectMockDependency(NavigationBarController.class);
@@ -230,7 +232,6 @@
                 mExpansionStateLogger);
         mNotificationLogger.setVisibilityReporter(mock(Runnable.class));
         mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
-        DozeLog.traceDozing(mContext, false /* dozing */);
 
         mCommandQueue = mock(CommandQueue.class);
         when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -652,7 +653,7 @@
 
         // Starting a pulse should change the scrim controller to the pulsing state
         mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
-                DozeLog.PULSE_REASON_NOTIFICATION);
+                DozeEvent.PULSE_REASON_NOTIFICATION);
         verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
 
         // Ending a pulse should take it back to keyguard state
@@ -663,21 +664,21 @@
     @Test
     public void testPulseWhileDozing_notifyAuthInterrupt() {
         HashSet<Integer> reasonsWantingAuth = new HashSet<>(
-                Collections.singletonList(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
+                Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
         HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
-                Arrays.asList(DozeLog.PULSE_REASON_INTENT,
-                        DozeLog.PULSE_REASON_NOTIFICATION,
-                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
-                        DozeLog.REASON_SENSOR_PICKUP,
-                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
-                        DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
-                        DozeLog.PULSE_REASON_DOCKING,
-                        DozeLog.REASON_SENSOR_WAKE_UP,
-                        DozeLog.REASON_SENSOR_TAP));
+                Arrays.asList(DozeEvent.PULSE_REASON_INTENT,
+                        DozeEvent.PULSE_REASON_NOTIFICATION,
+                        DozeEvent.PULSE_REASON_SENSOR_SIGMOTION,
+                        DozeEvent.REASON_SENSOR_PICKUP,
+                        DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+                        DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
+                        DozeEvent.PULSE_REASON_DOCKING,
+                        DozeEvent.REASON_SENSOR_WAKE_UP,
+                        DozeEvent.REASON_SENSOR_TAP));
         HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
-                Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
-                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
-                        DozeLog.REASON_SENSOR_TAP));
+                Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP,
+                        DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+                        DozeEvent.REASON_SENSOR_TAP));
 
         doAnswer(invocation -> {
             DozeHost.PulseCallback callback = invocation.getArgument(0);
@@ -686,7 +687,7 @@
         }).when(mDozeScrimController).pulse(any(), anyInt());
 
         mStatusBar.mDozeServiceHost.mWakeLockScreenPerformsAuth = true;
-        for (int i = 0; i < DozeLog.REASONS; i++) {
+        for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
             reset(mKeyguardUpdateMonitor);
             mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
             if (reasonsWantingAuth.contains(i)) {
@@ -711,7 +712,7 @@
 
         // Starting a pulse while docking should suppress wakeup gesture
         mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
-                DozeLog.PULSE_REASON_DOCKING);
+                DozeEvent.PULSE_REASON_DOCKING);
         verify(mStatusBarWindowView).suppressWakeUpGesture(eq(true));
 
         // Ending a pulse should restore wakeup gesture
@@ -907,9 +908,10 @@
                 IDreamManager dreamManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 NotificationFilter filter,
-                StatusBarStateController controller) {
+                StatusBarStateController controller,
+                BatteryController batteryController) {
             super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
-                    controller);
+                    batteryController, controller);
             mUseHeadsUp = true;
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index a843cca..df76f01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -48,4 +48,9 @@
     public boolean isPowerSave() {
         return false;
     }
+
+    @Override
+    public boolean isAodPowerSave() {
+        return false;
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 8e20019..b35300c 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -78,6 +78,8 @@
     private static final int THEME_ID_DARK =
             com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
 
+    private static final int SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS = 500;
+
     public interface OnSaveListener {
         void onSave();
         void onCancel(IntentSender listener);
@@ -252,6 +254,8 @@
                         new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                 ViewGroup.LayoutParams.WRAP_CONTENT));
                 subtitleContainer.setVisibility(View.VISIBLE);
+                subtitleContainer.setScrollBarDefaultDelayBeforeFade(
+                        SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS);
             }
             if (sDebug) Slog.d(TAG, "on constructor: title=" + mTitle + ", subTitle=" + mSubTitle);
         }
@@ -429,6 +433,9 @@
                     saveUiView.findViewById(R.id.autofill_save_custom_subtitle);
             subtitleContainer.addView(customSubtitleView);
             subtitleContainer.setVisibility(View.VISIBLE);
+            subtitleContainer.setScrollBarDefaultDelayBeforeFade(
+                    SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS);
+
             return true;
         } catch (Exception e) {
             Slog.e(TAG, "Error applying custom description. ", e);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f70d511..67686e0 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -197,7 +197,7 @@
             "persist.sys.zram_enabled";
 
     private static final boolean IS_FUSE_ENABLED =
-            SystemProperties.getBoolean("persist.sys.fuse", false);
+            SystemProperties.getBoolean(StorageManager.PROP_FUSE, false);
 
     private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
 
@@ -814,7 +814,7 @@
                 }
             });
         // For now, simply clone property when it changes
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE,
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                 mContext.getMainExecutor(), (properties) -> {
                     refreshIsolatedStorageSettings();
                 });
@@ -854,7 +854,8 @@
         // Always copy value from newer DeviceConfig location
         Settings.Global.putString(mResolver,
                 Settings.Global.ISOLATED_STORAGE_REMOTE,
-                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE, ISOLATED_STORAGE_ENABLED));
+                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                        ISOLATED_STORAGE_ENABLED));
 
         final int local = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
@@ -1526,6 +1527,9 @@
         SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
                 SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
 
+        SystemProperties.set(StorageManager.PROP_FUSE_SNAPSHOT, Boolean.toString(
+                SystemProperties.getBoolean(StorageManager.PROP_FUSE, false)));
+
         mContext = context;
         mResolver = mContext.getContentResolver();
 
@@ -1858,7 +1862,7 @@
             // This means the mountUserId on such volumes is USER_NULL. This breaks fuse which
             // requires a valid user to mount a volume. Create individual volumes per user in vold
             // and remove this property check
-            int userId = SystemProperties.getBoolean("persist.sys.fuse", false)
+            int userId = SystemProperties.getBoolean(StorageManager.PROP_FUSE_SNAPSHOT, false)
                     ? mCurrentUserId : vol.mountUserId;
             return mVold.mount(vol.id, vol.mountFlags, userId);
         } catch (Exception e) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 5947c35..ecbbef1 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -208,6 +208,10 @@
 
     private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
 
+    private EmergencyNumber[] mOutgoingSmsEmergencyNumber;
+
+    private EmergencyNumber[] mOutgoingCallEmergencyNumber;
+
     private CallQuality[] mCallQuality;
 
     private CallAttributes[] mCallAttributes;
@@ -266,6 +270,10 @@
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
                 PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
 
+    static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
+            PhoneStateListener.LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER
+                    | PhoneStateListener.LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER;
+
     private static final int MSG_USER_SWITCHED = 1;
     private static final int MSG_UPDATE_DEFAULT_SUB = 2;
 
@@ -406,6 +414,8 @@
         mImsReasonInfo = new ArrayList<>();
         mPhysicalChannelConfigs = new ArrayList<>();
         mEmergencyNumberList = new HashMap<>();
+        mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
+        mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -1910,6 +1920,56 @@
     }
 
     @Override
+    public void notifyOutgoingEmergencyCall(int phoneId, int subId,
+            EmergencyNumber emergencyNumber) {
+        if (!checkNotifyPermission("notifyOutgoingEmergencyCall()")) {
+            return;
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                mOutgoingCallEmergencyNumber[phoneId] = emergencyNumber;
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER)
+                                    && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onOutgoingEmergencyCall(emergencyNumber);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+    @Override
+    public void notifyOutgoingEmergencySms(int phoneId, int subId,
+            EmergencyNumber emergencyNumber) {
+        if (!checkNotifyPermission("notifyOutgoingEmergencySms()")) {
+            return;
+        }
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER)
+                                    && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onOutgoingEmergencySms(emergencyNumber);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+    @Override
     public void notifyCallQualityChanged(CallQuality callQuality, int phoneId, int subId,
             int callNetworkType) {
         if (!checkNotifyPermission("notifyCallQualityChanged()")) {
@@ -1981,6 +2041,8 @@
                 pw.println("mCallAttributes=" + mCallAttributes[i]);
                 pw.println("mCallNetworkType=" + mCallNetworkType[i]);
                 pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState[i]);
+                pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
+                pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -2250,6 +2312,11 @@
                     android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
         }
 
+        if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
+        }
+
         if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7b69bea..1cb91d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -233,6 +233,7 @@
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.BinderProxy;
+import android.os.BugreportParams;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
@@ -8255,22 +8256,22 @@
             @Nullable String shareDescription, int bugreportType) {
         String type = null;
         switch (bugreportType) {
-            case ActivityManager.BUGREPORT_OPTION_FULL:
+            case BugreportParams.BUGREPORT_MODE_FULL:
                 type = "bugreportfull";
                 break;
-            case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
+            case BugreportParams.BUGREPORT_MODE_INTERACTIVE:
                 type = "bugreportplus";
                 break;
-            case ActivityManager.BUGREPORT_OPTION_REMOTE:
+            case BugreportParams.BUGREPORT_MODE_REMOTE:
                 type = "bugreportremote";
                 break;
-            case ActivityManager.BUGREPORT_OPTION_WEAR:
+            case BugreportParams.BUGREPORT_MODE_WEAR:
                 type = "bugreportwear";
                 break;
-            case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
+            case BugreportParams.BUGREPORT_MODE_TELEPHONY:
                 type = "bugreporttelephony";
                 break;
-            case ActivityManager.BUGREPORT_OPTION_WIFI:
+            case BugreportParams.BUGREPORT_MODE_WIFI:
                 type = "bugreportwifi";
                 break;
             default:
@@ -8305,7 +8306,7 @@
         final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
                 FeatureFlagUtils.USE_BUGREPORT_API);
 
-        if (useApi && bugreportType == ActivityManager.BUGREPORT_OPTION_INTERACTIVE) {
+        if (useApi && bugreportType == BugreportParams.BUGREPORT_MODE_INTERACTIVE) {
             // Create intent to trigger Bugreport API via Shell
             Intent triggerShellBugreport = new Intent();
             triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
@@ -8341,7 +8342,7 @@
     @Override
     public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
         requestBugReportWithDescription(shareTitle, shareDescription,
-                ActivityManager.BUGREPORT_OPTION_TELEPHONY);
+                BugreportParams.BUGREPORT_MODE_TELEPHONY);
     }
 
     /**
@@ -8353,7 +8354,7 @@
     @Override
     public void requestWifiBugReport(String shareTitle, String shareDescription) {
         requestBugReportWithDescription(shareTitle, shareDescription,
-                ActivityManager.BUGREPORT_OPTION_WIFI);
+                BugreportParams.BUGREPORT_MODE_WIFI);
     }
 
     /**
@@ -8361,7 +8362,7 @@
      */
     @Override
     public void requestInteractiveBugReport() {
-        requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+        requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_INTERACTIVE);
     }
 
     /**
@@ -8372,7 +8373,7 @@
     public void requestInteractiveBugReportWithDescription(String shareTitle,
             String shareDescription) {
         requestBugReportWithDescription(shareTitle, shareDescription,
-                ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+                BugreportParams.BUGREPORT_MODE_INTERACTIVE);
     }
 
     /**
@@ -8380,7 +8381,7 @@
      */
     @Override
     public void requestFullBugReport() {
-        requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_FULL);
+        requestBugReportWithDescription(null, null,  BugreportParams.BUGREPORT_MODE_FULL);
     }
 
     /**
@@ -8388,7 +8389,7 @@
      */
     @Override
     public void requestRemoteBugReport() {
-        requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_REMOTE);
+        requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE);
     }
 
     public void registerProcessObserver(IProcessObserver observer) {
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index fd64df9..2081b17 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -39,8 +39,7 @@
  * Static utility methods related to {@link MemoryStat}.
  */
 public final class MemoryStatUtil {
-    static final int BYTES_IN_KILOBYTE = 1024;
-    static final long JIFFY_NANOS = 1_000_000_000 / Os.sysconf(OsConstants._SC_CLK_TCK);
+    static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
@@ -52,10 +51,6 @@
     private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
     /** Path to procfs stat file for logging app start memory state */
     private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
-    /** Path to procfs status file for logging app memory state */
-    private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
-    /** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
-    private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
 
     private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
     private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -63,16 +58,9 @@
     private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
     private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
 
-    private static final Pattern PROCFS_RSS_IN_KILOBYTES =
-            Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
-    private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES =
-            Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
-    private static final Pattern PROCFS_SWAP_IN_KILOBYTES =
-            Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
-
     private static final int PGFAULT_INDEX = 9;
     private static final int PGMAJFAULT_INDEX = 11;
-    private static final int START_TIME_INDEX = 21;
+    private static final int RSS_IN_PAGES_INDEX = 23;
 
     private MemoryStatUtil() {}
 
@@ -106,19 +94,7 @@
     @Nullable
     public static MemoryStat readMemoryStatFromProcfs(int pid) {
         final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
-        final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
-        return parseMemoryStatFromProcfs(readFileContents(statPath), readFileContents(statusPath));
-    }
-
-    /**
-     * Reads cmdline of a process from procfs.
-     *
-     * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
-     * if the file is not available.
-     */
-    public static String readCmdlineFromProcfs(int pid) {
-        final String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
-        return parseCmdlineFromProcfs(readFileContents(path));
+        return parseMemoryStatFromProcfs(readFileContents(statPath));
     }
 
     private static String readFileContents(String path) {
@@ -160,31 +136,19 @@
      */
     @VisibleForTesting
     @Nullable
-    static MemoryStat parseMemoryStatFromProcfs(
-            String procStatContents, String procStatusContents) {
+    static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
         if (procStatContents == null || procStatContents.isEmpty()) {
             return null;
         }
-        if (procStatusContents == null || procStatusContents.isEmpty()) {
-            return null;
-        }
-
         final String[] splits = procStatContents.split(" ");
         if (splits.length < 24) {
             return null;
         }
-
         try {
             final MemoryStat memoryStat = new MemoryStat();
             memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]);
             memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
-            memoryStat.rssInBytes =
-                tryParseLong(PROCFS_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
-            memoryStat.anonRssInBytes =
-                tryParseLong(PROCFS_ANON_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
-            memoryStat.swapInBytes =
-                tryParseLong(PROCFS_SWAP_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
-            memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS;
+            memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE;
             return memoryStat;
         } catch (NumberFormatException e) {
             Slog.e(TAG, "Failed to parse value", e);
@@ -193,23 +157,6 @@
     }
 
     /**
-     * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
-     *
-     * Parsing is required to strip anything after first null byte.
-     */
-    @VisibleForTesting
-    static String parseCmdlineFromProcfs(String cmdline) {
-        if (cmdline == null) {
-            return "";
-        }
-        int firstNullByte = cmdline.indexOf("\0");
-        if (firstNullByte == -1) {
-            return cmdline;
-        }
-        return cmdline.substring(0, firstNullByte);
-    }
-
-    /**
      * Returns whether per-app memcg is available on device.
      */
     static boolean hasMemcg() {
@@ -237,13 +184,9 @@
         public long pgmajfault;
         /** For memcg stats, the anon rss + swap cache size. Otherwise total RSS. */
         public long rssInBytes;
-        /** Number of bytes of the anonymous RSS. Only present for non-memcg stats. */
-        public long anonRssInBytes;
         /** Number of bytes of page cache memory. Only present for memcg stats. */
         public long cacheInBytes;
         /** Number of bytes of swap usage */
         public long swapInBytes;
-        /** Device time when the processes started. */
-        public long startTimeNanos;
     }
 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 64f4a35..4a6e63f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -86,6 +86,7 @@
         DeviceConfig.NAMESPACE_NETD_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
     };
 
     private final String[] mGlobalSettings;
@@ -276,4 +277,4 @@
         String settingValue = Settings.Global.getString(mContentResolver, settingName);
         setProperty(propName, settingValue);
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 77b3feec..ccbe08f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3854,7 +3854,7 @@
         final boolean muteSystem = (zenPolicy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0;
         final boolean muteNotificationAndRing = ZenModeConfig
-                .areAllPriorityOnlyNotificationZenSoundsMuted(
+                .areAllPriorityOnlyRingerSoundsMuted(
                         mNm.getConsolidatedNotificationPolicy());
         return muteAlarms && isAlarm(streamType)
                 || muteMedia && isMedia(streamType)
@@ -3867,16 +3867,26 @@
     }
 
     /**
-     * DND total silence: media and alarms streams are tied to the muted ringer
+     * Notifications, ringer and system sounds are controlled by the ringer:
      * {@link ZenModeHelper.RingerModeDelegate#getRingerModeAffectedStreams(int)}
-     * DND alarms only: notification, ringer + system muted (by default tied to muted ringer mode)
-     * DND priority only: alarms, media, system streams can be muted separate from ringer based on
+     * DND total silence: media and alarms streams can be muted by DND
+     * DND alarms only: no streams additionally controlled by DND
+     * DND priority only: alarms, media, system streams can be muted by DND based on
      * zenPolicy (this method determines which streams)
      * @return true if changed, else false
      */
     private boolean updateZenModeAffectedStreams() {
+        if (!mSystemReady) {
+            return false;
+        }
+
         int zenModeAffectedStreams = 0;
-        if (mSystemReady && mNm.getZenMode() == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+        final int zenMode = mNm.getZenMode();
+
+        if (zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_ALARM;
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
+        } else if (zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
             NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
             if ((zenPolicy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0) {
@@ -3888,6 +3898,8 @@
                 zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
             }
 
+            // even if zen isn't muting the system stream, the ringer mode can still mute
+            // the system stream
             if ((zenPolicy.priorityCategories
                     & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0) {
                 zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index b1c7c76..9eb0d50 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -930,7 +930,8 @@
                     final Face face = new Face("", 0 /* identifier */, deviceId);
                     FaceService.super.handleRemoved(face, 0 /* remaining */);
                 }
-
+                Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                        Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
             });
         }
 
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index cfbf8bc..96ba8ef 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -394,10 +394,6 @@
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
-    // Property name for the local device configurations.
-    // TODO(OEM): OEM should provide this property, and the value is the comma separated integer
-    //     values which denotes the device type in HDMI Spec 1.4.
-    static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
 
     // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
     //            True by default.
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3856de4..c3354e1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,8 +56,8 @@
 import android.media.tv.TvInputManager;
 import android.media.tv.TvInputManager.TvInputCallback;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -68,6 +68,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -95,6 +96,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Provides a service for sending and processing HDMI control messages,
@@ -452,7 +455,14 @@
 
     public HdmiControlService(Context context) {
         super(context);
-        mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
+        List<Integer> deviceTypes = HdmiProperties.device_type();
+        if (deviceTypes.contains(null)) {
+            Slog.w(TAG, "Error parsing ro.hdmi.device.type: " + SystemProperties.get(
+                    "ro.hdmi.device_type"));
+            deviceTypes = deviceTypes.stream().filter(Objects::nonNull).collect(
+                    Collectors.toList());
+        }
+        mLocalDevices = deviceTypes;
         mSettingsObserver = new SettingsObserver(mHandler);
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 75b9705..8fbad4c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -220,6 +220,8 @@
     private static native void nativeSetFocusedApplication(long ptr,
             int displayId, InputApplicationHandle application);
     private static native void nativeSetFocusedDisplay(long ptr, int displayId);
+    private static native boolean nativeTransferTouchFocus(long ptr,
+            InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
     private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1533,6 +1535,29 @@
         nativeSetSystemUiVisibility(mPtr, visibility);
     }
 
+    /**
+     * Atomically transfers touch focus from one window to another as identified by
+     * their input channels.  It is possible for multiple windows to have
+     * touch focus if they support split touch dispatch
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+     * method only transfers touch focus of the specified window without affecting
+     * other windows that may also have touch focus at the same time.
+     * @param fromChannel The channel of a window that currently has touch focus.
+     * @param toChannel The channel of the window that should receive touch focus in
+     * place of the first.
+     * @return True if the transfer was successful.  False if the window with the
+     * specified channel did not actually have touch focus at the time of the request.
+     */
+    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
+        if (fromChannel == null) {
+            throw new IllegalArgumentException("fromChannel must not be null.");
+        }
+        if (toChannel == null) {
+            throw new IllegalArgumentException("toChannel must not be null.");
+        }
+        return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
+    }
+
     @Override // Binder call
     public void tryPointerSpeed(int speed) {
         if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b7eca29..378d9eb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -44,6 +44,7 @@
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
 import android.app.admin.PasswordMetrics;
 import android.app.backup.BackupManager;
 import android.app.trust.IStrongAuthTracker;
@@ -76,6 +77,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -429,6 +431,10 @@
             return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         }
 
+        public UserManagerInternal getUserManagerInternal() {
+            return LocalServices.getService(UserManagerInternal.class);
+        }
+
         /**
          * Return the {@link DevicePolicyManager} object.
          *
@@ -440,6 +446,10 @@
             return (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         }
 
+        public DeviceStateCache getDeviceStateCache() {
+            return DeviceStateCache.getInstance();
+        }
+
         public KeyStore getKeyStore() {
             return KeyStore.getInstance();
         }
@@ -1023,11 +1033,16 @@
     }
 
     private void notifySeparateProfileChallengeChanged(int userId) {
-        final DevicePolicyManagerInternal dpmi = LocalServices.getService(
-                DevicePolicyManagerInternal.class);
-        if (dpmi != null) {
-            dpmi.reportSeparateProfileChallengeChanged(userId);
-        }
+        // LSS cannot call into DPM directly, otherwise it will cause deadlock.
+        // In this case, calling DPM on a handler thread is OK since DPM doesn't
+        // expect reportSeparateProfileChallengeChanged() to happen synchronously.
+        mHandler.post(() -> {
+            final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+                    DevicePolicyManagerInternal.class);
+            if (dpmi != null) {
+                dpmi.reportSeparateProfileChallengeChanged(userId);
+            }
+        });
     }
 
     @Override
@@ -2038,7 +2053,6 @@
      * reporting the password changed.
      */
     private void notifyPasswordChanged(@UserIdInt int userId) {
-        // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
         mHandler.post(() -> {
             mInjector.getDevicePolicyManager().reportPasswordChanged(userId);
             LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
@@ -3026,45 +3040,43 @@
         pw.decreaseIndent();
     }
 
+    /**
+     * Cryptographically disable escrow token support for the current user, if the user is not
+     * managed (either user has a profile owner, or if device is managed). Do not disable
+     * if we are running an automotive build.
+     */
     private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            // Managed profile should have escrow enabled
-            if (mUserManager.getUserInfo(userId).isManagedProfile()) {
-                Slog.i(TAG, "Managed profile can have escrow token");
-                return;
-            }
-            DevicePolicyManager dpm = mInjector.getDevicePolicyManager();
-            // Devices with Device Owner should have escrow enabled on all users.
-            if (dpm.getDeviceOwnerComponentOnAnyUser() != null) {
-                Slog.i(TAG, "Corp-owned device can have escrow token");
-                return;
-            }
-            // We could also have a profile owner on the given (non-managed) user for unicorn cases
-            if (dpm.getProfileOwnerAsUser(userId) != null) {
-                Slog.i(TAG, "User with profile owner can have escrow token");
-                return;
-            }
-            // If the device is yet to be provisioned (still in SUW), there is still
-            // a chance that Device Owner will be set on the device later, so postpone
-            // disabling escrow token for now.
-            if (!dpm.isDeviceProvisioned()) {
-                Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
-                return;
-            }
+        final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
 
-            // Escrow tokens are enabled on automotive builds.
-            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
-                return;
-            }
+        // Managed profile should have escrow enabled
+        if (userManagerInternal.isUserManaged(userId)) {
+            Slog.i(TAG, "Managed profile can have escrow token");
+            return;
+        }
 
-            // Disable escrow token permanently on all other device/user types.
-            Slog.i(TAG, "Disabling escrow token on user " + userId);
-            if (isSyntheticPasswordBasedCredentialLocked(userId)) {
-                mSpManager.destroyEscrowData(userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+        // Devices with Device Owner should have escrow enabled on all users.
+        if (userManagerInternal.isDeviceManaged()) {
+            Slog.i(TAG, "Corp-owned device can have escrow token");
+            return;
+        }
+
+        // If the device is yet to be provisioned (still in SUW), there is still
+        // a chance that Device Owner will be set on the device later, so postpone
+        // disabling escrow token for now.
+        if (!mInjector.getDeviceStateCache().isDeviceProvisioned()) {
+            Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
+            return;
+        }
+
+        // Escrow tokens are enabled on automotive builds.
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            return;
+        }
+
+        // Disable escrow token permanently on all other device/user types.
+        Slog.i(TAG, "Disabling escrow token on user " + userId);
+        if (isSyntheticPasswordBasedCredentialLocked(userId)) {
+            mSpManager.destroyEscrowData(userId);
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index ea0fb47..9246311 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -129,6 +129,9 @@
             keyStore.load(null);
 
             SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+            if (decryptionKey == null) {
+                throw new IllegalStateException("SP key is missing: " + keyAlias);
+            }
             byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
             return decrypt(decryptionKey, intermediate);
         } catch (Exception e) {
@@ -143,6 +146,9 @@
             keyStore.load(null);
 
             SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+            if (decryptionKey == null) {
+                throw new IllegalStateException("SP key is missing: " + keyAlias);
+            }
             byte[] intermediate = decrypt(decryptionKey, blob);
             return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
         } catch (CertificateException | IOException | BadPaddingException
@@ -193,6 +199,7 @@
             keyStore = KeyStore.getInstance("AndroidKeyStore");
             keyStore.load(null);
             keyStore.deleteEntry(keyAlias);
+            Slog.i(TAG, "SP key deleted: " + keyAlias);
         } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
                 | IOException e) {
             Slog.e(TAG, "Failed to destroy blob", e);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f63aa52..6510923 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1179,7 +1179,7 @@
 
             if (mZenMode == Global.ZEN_MODE_OFF
                     || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                    && !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig))) {
+                    && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
                 // in priority only with ringer not muted, save ringer mode changes
                 // in dnd off, save ringer mode changes
                 setPreviousRingerModeSetting(ringerModeNew);
@@ -1200,7 +1200,7 @@
                             && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
                             || mZenMode == Global.ZEN_MODE_ALARMS
                             || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                            && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+                            && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
                             mConfig)))) {
                         newZen = Global.ZEN_MODE_OFF;
                     } else if (mZenMode != Global.ZEN_MODE_OFF) {
@@ -1264,29 +1264,21 @@
 
         @Override
         public int getRingerModeAffectedStreams(int streams) {
-            // ringtone and notification streams are always affected by ringer mode
-            // system stream is affected by ringer mode when not in priority-only
+            // ringtone, notification and system streams are always affected by ringer mode
+            // zen muting is handled in AudioService.java's mZenModeAffectedStreams
             streams |= (1 << AudioSystem.STREAM_RING) |
                     (1 << AudioSystem.STREAM_NOTIFICATION) |
                     (1 << AudioSystem.STREAM_SYSTEM);
 
             if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
-                // alarm and music streams affected by ringer mode when in total silence
+                // alarm and music streams affected by ringer mode (cannot be adjusted) when in
+                // total silence
                 streams |= (1 << AudioSystem.STREAM_ALARM) |
                         (1 << AudioSystem.STREAM_MUSIC);
             } else {
                 streams &= ~((1 << AudioSystem.STREAM_ALARM) |
                         (1 << AudioSystem.STREAM_MUSIC));
             }
-
-            if (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                    && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) {
-                // system stream is not affected by ringer mode in priority only when the ringer
-                // is zen muted (all other notification categories are muted)
-                streams &= ~(1 << AudioSystem.STREAM_SYSTEM);
-            } else {
-                streams |= (1 << AudioSystem.STREAM_SYSTEM);
-            }
             return streams;
         }
     }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 61ea84f..86c709e0 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageParser.Component;
-import static android.content.pm.PackageParser.IntentInfo;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
 import android.Manifest;
@@ -25,8 +23,11 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.ProviderInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Process;
@@ -40,14 +41,15 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.FgThread;
 import com.android.server.compat.PlatformCompat;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -125,8 +127,7 @@
         boolean isGloballyEnabled();
 
         /** @return true if the feature is enabled for the given package. */
-        boolean packageIsEnabled(PackageParser.Package pkg);
-
+        boolean packageIsEnabled(AndroidPackage pkg);
     }
 
     private static class FeatureConfigImpl implements FeatureConfig {
@@ -159,14 +160,14 @@
         }
 
         @Override
-        public boolean packageIsEnabled(PackageParser.Package pkg) {
+        public boolean packageIsEnabled(AndroidPackage pkg) {
             final PlatformCompat compatibility = mInjector.getCompatibility();
             if (compatibility == null) {
                 Slog.wtf(TAG, "PlatformCompat is null");
                 return mFeatureEnabled;
             }
-            return compatibility.isChangeEnabled(
-                    PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
+            return compatibility.isChangeEnabled(PackageManager.FILTER_APPLICATION_QUERY,
+                    pkg.toAppInfo());
         }
     }
 
@@ -196,14 +197,13 @@
     }
 
     /** Returns true if the querying package may query for the potential target package */
-    private static boolean canQuery(PackageParser.Package querying,
-            PackageParser.Package potentialTarget) {
-        if (querying.mQueriesIntents == null) {
+    private static boolean canQuery(AndroidPackage querying, AndroidPackage potentialTarget) {
+        if (querying.getQueriesIntents() == null || potentialTarget.getActivities() == null) {
             return false;
         }
-        for (Intent intent : querying.mQueriesIntents) {
-            if (matches(intent, potentialTarget.providers, potentialTarget.activities,
-                    potentialTarget.services, potentialTarget.receivers)) {
+        for (Intent intent : querying.getQueriesIntents()) {
+            if (matches(intent, potentialTarget.getProviders(), potentialTarget.getActivities(),
+                    potentialTarget.getServices(), potentialTarget.getReceivers())) {
                 return true;
             }
         }
@@ -211,24 +211,24 @@
     }
 
     private static boolean matches(Intent intent,
-            ArrayList<PackageParser.Provider> providerList,
-            ArrayList<? extends Component<? extends IntentInfo>>... componentLists) {
-        for (int p = providerList.size() - 1; p >= 0; p--) {
-            PackageParser.Provider provider = providerList.get(p);
-            final ProviderInfo providerInfo = provider.info;
+            @Nullable List<ParsedProvider> providerList,
+            List<? extends ParsedComponent<? extends ParsedIntentInfo>>... componentLists) {
+        for (int p = ArrayUtils.size(providerList) - 1; p >= 0; p--) {
+            ParsedProvider provider = providerList.get(p);
             final Uri data = intent.getData();
             if ("content".equalsIgnoreCase(intent.getScheme())
                     && data != null
-                    && providerInfo.authority.equalsIgnoreCase(data.getAuthority())) {
+                    && provider.getAuthority().equalsIgnoreCase(data.getAuthority())) {
                 return true;
             }
         }
 
         for (int l = componentLists.length - 1; l >= 0; l--) {
-            ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l];
+            List<? extends ParsedComponent<? extends ParsedIntentInfo>> components =
+                    componentLists[l];
             for (int c = components.size() - 1; c >= 0; c--) {
-                Component<? extends IntentInfo> component = components.get(c);
-                ArrayList<? extends IntentInfo> intents = component.intents;
+                ParsedComponent<? extends ParsedIntentInfo> component = components.get(c);
+                List<? extends ParsedIntentInfo> intents = component.intents;
                 for (int i = intents.size() - 1; i >= 0; i--) {
                     IntentFilter intentFilter = intents.get(i);
                     if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
@@ -273,44 +273,44 @@
      * @param newPkg   the new package being added
      * @param existing all other packages currently on the device.
      */
-    public void addPackage(PackageParser.Package newPkg,
-            Map<String, PackageParser.Package> existing) {
+    public void addPackage(AndroidPackage newPkg, Map<String, AndroidPackage> existing) {
         // let's re-evaluate the ability of already added packages to see this new package
-        if (newPkg.mForceQueryable
+        if (newPkg.isForceQueryable()
                 || (mSystemAppsQueryable && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
-            mForceQueryable.add(newPkg.packageName);
+            mForceQueryable.add(newPkg.getPackageName());
         } else {
             for (String packageName : mQueriesViaIntent.keySet()) {
-                if (packageName == newPkg.packageName) {
+                if (Objects.equals(packageName, newPkg.getPackageName())) {
                     continue;
                 }
-                final PackageParser.Package existingPackage = existing.get(packageName);
+                final AndroidPackage existingPackage = existing.get(packageName);
                 if (canQuery(existingPackage, newPkg)) {
-                    mQueriesViaIntent.get(packageName).add(newPkg.packageName);
+                    mQueriesViaIntent.get(packageName).add(newPkg.getPackageName());
                 }
             }
         }
         // if the new package declares them, let's evaluate its ability to see existing packages
-        mQueriesViaIntent.put(newPkg.packageName, new HashSet<>());
-        for (PackageParser.Package existingPackage : existing.values()) {
-            if (existingPackage.packageName == newPkg.packageName) {
+        mQueriesViaIntent.put(newPkg.getPackageName(), new HashSet<>());
+        for (AndroidPackage existingPackage : existing.values()) {
+            if (Objects.equals(existingPackage.getPackageName(), newPkg.getPackageName())) {
                 continue;
             }
-            if (existingPackage.mForceQueryable
+            if (existingPackage.isForceQueryable()
                     || (mSystemAppsQueryable
                     && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
                 continue;
             }
             if (canQuery(newPkg, existingPackage)) {
-                mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName);
+                mQueriesViaIntent.get(newPkg.getPackageName())
+                        .add(existingPackage.getPackageName());
             }
         }
         final HashSet<String> queriesPackages = new HashSet<>(
-                newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size());
-        if (newPkg.mQueriesPackages != null) {
-            queriesPackages.addAll(newPkg.mQueriesPackages);
+                newPkg.getQueriesPackages() == null ? 0 : newPkg.getQueriesPackages().size());
+        if (newPkg.getQueriesPackages() != null) {
+            queriesPackages.addAll(newPkg.getQueriesPackages());
         }
-        mQueriesViaPackage.put(newPkg.packageName, queriesPackages);
+        mQueriesViaPackage.put(newPkg.getPackageName(), queriesPackages);
     }
 
     /**
@@ -405,8 +405,8 @@
 
     private boolean shouldFilterApplicationInternal(
             PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId) {
-        final String callingName = callingPkgSetting.pkg.packageName;
-        final PackageParser.Package targetPkg = targetPkgSetting.pkg;
+        final String callingName = callingPkgSetting.pkg.getPackageName();
+        final AndroidPackage targetPkg = targetPkgSetting.pkg;
 
         if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
             if (DEBUG_LOGGING) {
@@ -422,8 +422,8 @@
             }
             return true;
         }
-        final String targetName = targetPkg.packageName;
-        if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
+        final String targetName = targetPkg.getPackageName();
+        if (callingPkgSetting.pkg.getTargetSdkVersion() < Build.VERSION_CODES.R) {
             if (DEBUG_LOGGING) {
                 log(callingPkgSetting, targetPkgSetting, "caller pre-R");
             }
@@ -435,7 +435,7 @@
             }
             return false;
         }
-        if (targetPkg.mForceQueryable) {
+        if (targetPkg.isForceQueryable()) {
             if (DEBUG_LOGGING) {
                 log(callingPkgSetting, targetPkgSetting, "manifest forceQueryable");
             }
@@ -470,9 +470,11 @@
             }
             return false;
         }
-        if (callingPkgSetting.pkg.instrumentation.size() > 0) {
-            for (int i = 0, max = callingPkgSetting.pkg.instrumentation.size(); i < max; i++) {
-                if (callingPkgSetting.pkg.instrumentation.get(i).info.targetPackage == targetName) {
+        List<ComponentParseUtils.ParsedInstrumentation> instrumentations =
+                callingPkgSetting.pkg.getInstrumentations();
+        if (ArrayUtils.size(instrumentations) > 0) {
+            for (int i = 0, max = instrumentations.size(); i < max; i++) {
+                if (Objects.equals(instrumentations.get(i).getTargetPackage(), targetName)) {
                     if (DEBUG_LOGGING) {
                         log(callingPkgSetting, targetPkgSetting, "instrumentation");
                     }
@@ -504,7 +506,7 @@
 
     private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) {
         return targetPkgSetting.isSystem() && (mSystemAppsQueryable
-                || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
+                || mForceQueryableByDevice.contains(targetPkgSetting.pkg.getPackageName()));
     }
 
     public void dumpQueries(
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 8facce1..333a6e9 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -21,7 +21,6 @@
 
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
-import static com.android.server.pm.PackageManagerService.fixProcessName;
 
 import android.content.ComponentName;
 import android.content.Intent;
@@ -32,13 +31,18 @@
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ActivityIntentInfo;
-import android.content.pm.PackageParser.ServiceIntentInfo;
 import android.content.pm.PackageUserState;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProviderIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.pm.parsing.ComponentParseUtils.ParsedServiceIntentInfo;
+import android.content.pm.parsing.PackageInfoUtils;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -49,15 +53,19 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
 
 /** Resolves all Android component types [activities, services, providers and receivers]. */
 public class ComponentResolver {
@@ -158,7 +166,7 @@
 
     /** All available receivers, for your resolving pleasure. */
     @GuardedBy("mLock")
-    private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
+    private final ActivityIntentResolver mReceivers = new ReceiverIntentResolver();
 
     /** All available services, for your resolving pleasure. */
     @GuardedBy("mLock")
@@ -166,7 +174,7 @@
 
     /** Mapping from provider authority [first directory in content URI codePath) to provider. */
     @GuardedBy("mLock")
-    private final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = new ArrayMap<>();
+    private final ArrayMap<String, ParsedProvider> mProvidersByAuthority = new ArrayMap<>();
 
     /** Whether or not processing protected filters should be deferred. */
     private boolean mDeferProtectedFilters = true;
@@ -181,7 +189,7 @@
      * /system partition in order to know which component is the setup wizard. This can
      * only ever be non-empty if {@link #mDeferProtectedFilters} is {@code true}.
      */
-    private List<PackageParser.ActivityIntentInfo> mProtectedFilters;
+    private List<ParsedActivityIntentInfo> mProtectedFilters;
 
     ComponentResolver(UserManagerService userManager,
             PackageManagerInternal packageManagerInternal,
@@ -192,28 +200,28 @@
     }
 
     /** Returns the given activity */
-    PackageParser.Activity getActivity(ComponentName component) {
+    ParsedActivity getActivity(ComponentName component) {
         synchronized (mLock) {
             return mActivities.mActivities.get(component);
         }
     }
 
     /** Returns the given provider */
-    PackageParser.Provider getProvider(ComponentName component) {
+    ParsedProvider getProvider(ComponentName component) {
         synchronized (mLock) {
             return mProviders.mProviders.get(component);
         }
     }
 
     /** Returns the given receiver */
-    PackageParser.Activity getReceiver(ComponentName component) {
+    ParsedActivity getReceiver(ComponentName component) {
         synchronized (mLock) {
             return mReceivers.mActivities.get(component);
         }
     }
 
     /** Returns the given service */
-    PackageParser.Service getService(ComponentName component) {
+    ParsedService getService(ComponentName component) {
         synchronized (mLock) {
             return mServices.mServices.get(component);
         }
@@ -226,7 +234,7 @@
     }
 
     List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
-            List<PackageParser.Activity> activities, int userId) {
+            List<ParsedActivity> activities, int userId) {
         synchronized (mLock) {
             return mActivities.queryIntentForPackage(
                     intent, resolvedType, flags, activities, userId);
@@ -240,7 +248,7 @@
     }
 
     List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags,
-            List<PackageParser.Provider> providers, int userId) {
+            List<ParsedProvider> providers, int userId) {
         synchronized (mLock) {
             return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId);
         }
@@ -254,25 +262,34 @@
         List<ProviderInfo> providerList = null;
         synchronized (mLock) {
             for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
-                final PackageParser.Provider p = mProviders.mProviders.valueAt(i);
-                final PackageSetting ps = (PackageSetting) p.owner.mExtras;
+                final ParsedProvider p = mProviders.mProviders.valueAt(i);
+                if (p.getAuthority() == null) {
+                    continue;
+                }
+
+                final PackageSetting ps =
+                        (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                                p.getPackageName());
                 if (ps == null) {
                     continue;
                 }
-                if (p.info.authority == null) {
+
+                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+                if (pkg == null) {
                     continue;
                 }
-                if (processName != null && (!p.info.processName.equals(processName)
-                        || !UserHandle.isSameApp(p.info.applicationInfo.uid, uid))) {
+
+                if (processName != null && (!p.getProcessName().equals(processName)
+                        || !UserHandle.isSameApp(pkg.getUid(), uid))) {
                     continue;
                 }
                 // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
                 if (metaDataKey != null
-                        && (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {
+                        && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
                     continue;
                 }
-                final ProviderInfo info = PackageParser.generateProviderInfo(
-                        p, flags, ps.readUserState(userId), userId);
+                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                        pkg, p, flags, ps.readUserState(userId), userId);
                 if (info == null) {
                     continue;
                 }
@@ -285,17 +302,23 @@
         return providerList;
     }
 
-    ProviderInfo queryProvider(String authority, int flags, int userId) {
+    ProviderInfo queryProvider(String authority, int flags, int uid, int userId) {
         synchronized (mLock) {
-            final PackageParser.Provider p = mProvidersByAuthority.get(authority);
+            final ParsedProvider p = mProvidersByAuthority.get(authority);
             if (p == null) {
                 return null;
             }
-            final PackageSetting ps = (PackageSetting) p.owner.mExtras;
+            final PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    p.getPackageName());
             if (ps == null) {
                 return null;
             }
-            return PackageParser.generateProviderInfo(p, flags, ps.readUserState(userId), userId);
+            final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+            if (pkg == null) {
+                return null;
+            }
+            return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
+                    ps.readUserState(userId), userId);
         }
     }
 
@@ -303,20 +326,29 @@
             int userId) {
         synchronized (mLock) {
             for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
-                final PackageParser.Provider p = mProvidersByAuthority.valueAt(i);
-                final PackageSetting ps = (PackageSetting) p.owner.mExtras;
+                final ParsedProvider p = mProvidersByAuthority.valueAt(i);
+                if (!p.isSyncable()) {
+                    continue;
+                }
+
+                final PackageSetting ps =
+                        (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                                p.getPackageName());
                 if (ps == null) {
                     continue;
                 }
-                if (!p.syncable) {
+
+                final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+                if (pkg == null) {
                     continue;
                 }
-                if (safeMode
-                        && (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+
+                if (safeMode && (pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
                     continue;
                 }
                 final ProviderInfo info =
-                        PackageParser.generateProviderInfo(p, 0, ps.readUserState(userId), userId);
+                        PackageInfoUtils.generateProviderInfo(pkg, p, 0,
+                                ps.readUserState(userId), userId);
                 if (info == null) {
                     continue;
                 }
@@ -333,7 +365,7 @@
     }
 
     List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags,
-            List<PackageParser.Activity> receivers, int userId) {
+            List<ParsedActivity> receivers, int userId) {
         synchronized (mLock) {
             return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
         }
@@ -346,7 +378,7 @@
     }
 
     List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags,
-            List<PackageParser.Service> services, int userId) {
+            List<ParsedService> services, int userId) {
         synchronized (mLock) {
             return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId);
         }
@@ -360,15 +392,15 @@
     }
 
     /** Asserts none of the providers defined in the given package haven't already been defined. */
-    void assertProvidersNotDefined(PackageParser.Package pkg) throws PackageManagerException {
+    void assertProvidersNotDefined(AndroidPackage pkg) throws PackageManagerException {
         synchronized (mLock) {
             assertProvidersNotDefinedLocked(pkg);
         }
     }
 
     /** Add all components defined in the given package to the internal structures. */
-    void addAllComponents(PackageParser.Package pkg, boolean chatty) {
-        final ArrayList<PackageParser.ActivityIntentInfo> newIntents = new ArrayList<>();
+    void addAllComponents(AndroidPackage pkg, boolean chatty) {
+        final ArrayList<ParsedActivityIntentInfo> newIntents = new ArrayList<>();
         synchronized (mLock) {
             addActivitiesLocked(pkg, newIntents, chatty);
             addReceiversLocked(pkg, chatty);
@@ -378,17 +410,19 @@
         final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
                 PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
         for (int i = newIntents.size() - 1; i >= 0; --i) {
-            final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
-            final PackageParser.Package disabledPkg = sPackageManagerInternal
-                    .getDisabledSystemPackage(intentInfo.activity.info.packageName);
-            final List<PackageParser.Activity> systemActivities =
-                    disabledPkg != null ? disabledPkg.activities : null;
+            final ParsedActivityIntentInfo intentInfo = newIntents.get(i);
+            final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
+                    .getDisabledSystemPackage(intentInfo.getPackageName());
+            final AndroidPackage disabledPkg =
+                    disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
+            final List<ParsedActivity> systemActivities =
+                    disabledPkg != null ? disabledPkg.getActivities() : null;
             adjustPriority(systemActivities, intentInfo, setupWizardPackage);
         }
     }
 
     /** Removes all components defined in the given package from the internal structures. */
-    void removeAllComponents(PackageParser.Package pkg, boolean chatty) {
+    void removeAllComponents(AndroidPackage pkg, boolean chatty) {
         synchronized (mLock) {
             removeAllComponentsLocked(pkg, chatty);
         }
@@ -407,7 +441,7 @@
         if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
             return;
         }
-        final List<ActivityIntentInfo> protectedFilters = mProtectedFilters;
+        final List<ParsedActivityIntentInfo> protectedFilters = mProtectedFilters;
         mProtectedFilters = null;
 
         final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
@@ -417,13 +451,13 @@
                     + " All protected intents capped to priority 0");
         }
         for (int i = protectedFilters.size() - 1; i >= 0; --i) {
-            final ActivityIntentInfo filter = protectedFilters.get(i);
-            if (filter.activity.info.packageName.equals(setupWizardPackage)) {
+            final ParsedActivityIntentInfo filter = protectedFilters.get(i);
+            if (filter.getPackageName().equals(setupWizardPackage)) {
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Found setup wizard;"
                             + " allow priority " + filter.getPriority() + ";"
-                            + " package: " + filter.activity.info.packageName
-                            + " activity: " + filter.activity.className
+                            + " package: " + filter.getPackageName()
+                            + " activity: " + filter.getClassName()
                             + " priority: " + filter.getPriority());
                 }
                 // skip setup wizard; allow it to keep the high priority filter
@@ -431,8 +465,8 @@
             }
             if (DEBUG_FILTERS) {
                 Slog.i(TAG, "Protected action; cap priority to 0;"
-                        + " package: " + filter.activity.info.packageName
-                        + " activity: " + filter.activity.className
+                        + " package: " + filter.getPackageName()
+                        + " activity: " + filter.getClassName()
                         + " origPrio: " + filter.getPriority());
             }
             filter.setPriority(0);
@@ -473,8 +507,8 @@
 
     void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
         boolean printedSomething = false;
-        for (PackageParser.Provider p : mProviders.mProviders.values()) {
-            if (packageName != null && !packageName.equals(p.info.packageName)) {
+        for (ParsedProvider p : mProviders.mProviders.values()) {
+            if (packageName != null && !packageName.equals(p.getPackageName())) {
                 continue;
             }
             if (!printedSomething) {
@@ -484,14 +518,17 @@
                 pw.println("Registered ContentProviders:");
                 printedSomething = true;
             }
-            pw.print("  "); p.printComponentShortName(pw); pw.println(":");
-            pw.print("    "); pw.println(p.toString());
+            pw.print("  ");
+            ComponentName.printShortString(pw, p.getPackageName(), p.className);
+            pw.println(":");
+            pw.print("    ");
+            pw.println(p.toString());
         }
         printedSomething = false;
-        for (Map.Entry<String, PackageParser.Provider> entry :
+        for (Map.Entry<String, ParsedProvider> entry :
                 mProvidersByAuthority.entrySet()) {
-            PackageParser.Provider p = entry.getValue();
-            if (packageName != null && !packageName.equals(p.info.packageName)) {
+            ParsedProvider p = entry.getValue();
+            if (packageName != null && !packageName.equals(p.getPackageName())) {
                 continue;
             }
             if (!printedSomething) {
@@ -503,25 +540,43 @@
             }
             pw.print("  ["); pw.print(entry.getKey()); pw.println("]:");
             pw.print("    "); pw.println(p.toString());
-            if (p.info != null && p.info.applicationInfo != null) {
-                final String appInfo = p.info.applicationInfo.toString();
-                pw.print("      applicationInfo="); pw.println(appInfo);
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+
+            if (pkg != null) {
+                // TODO(b/135203078): Print AppInfo?
+                pw.print("      applicationInfo="); pw.println(pkg.toAppInfo());
             }
         }
     }
 
-    void dumpServicePermissions(PrintWriter pw, DumpState dumpState, String packageName) {
+    void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
         if (dumpState.onTitlePrinted()) pw.println();
         pw.println("Service permissions:");
 
-        final Iterator<ServiceIntentInfo> filterIterator = mServices.filterIterator();
+        final Iterator<ParsedServiceIntentInfo> filterIterator = mServices.filterIterator();
         while (filterIterator.hasNext()) {
-            final ServiceIntentInfo info = filterIterator.next();
-            final ServiceInfo serviceInfo = info.service.info;
-            final String permission = serviceInfo.permission;
+            final ParsedServiceIntentInfo info = filterIterator.next();
+
+            ParsedService service = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(info.getPackageName());
+            if (pkg != null && pkg.getServices() != null) {
+                for (ParsedService parsedService : pkg.getServices()) {
+                    if (Objects.equals(parsedService.className, info.getClassName())) {
+                        service = parsedService;
+                    }
+                }
+            }
+
+            if (service == null) {
+                continue;
+            }
+
+            final String permission = service.getPermission();
             if (permission != null) {
                 pw.print("    ");
-                pw.print(serviceInfo.getComponentName().flattenToShortString());
+                pw.print(service.getComponentName().flattenToShortString());
                 pw.print(": ");
                 pw.println(permission);
             }
@@ -529,14 +584,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addActivitiesLocked(PackageParser.Package pkg,
-            List<PackageParser.ActivityIntentInfo> newIntents, boolean chatty) {
-        final int activitiesSize = pkg.activities.size();
+    private void addActivitiesLocked(AndroidPackage pkg,
+            List<ParsedActivityIntentInfo> newIntents, boolean chatty) {
+        final int activitiesSize = ArrayUtils.size(pkg.getActivities());
         StringBuilder r = null;
         for (int i = 0; i < activitiesSize; i++) {
-            PackageParser.Activity a = pkg.activities.get(i);
-            a.info.processName =
-                    fixProcessName(pkg.applicationInfo.processName, a.info.processName);
+            ParsedActivity a = pkg.getActivities().get(i);
             mActivities.addActivity(a, "activity", newIntents);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
@@ -544,7 +597,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(a.info.name);
+                r.append(a.getName());
             }
         }
         if (DEBUG_PACKAGE_SCANNING && chatty) {
@@ -553,20 +606,17 @@
     }
 
     @GuardedBy("mLock")
-    private void addProvidersLocked(PackageParser.Package pkg, boolean chatty) {
-        final int providersSize = pkg.providers.size();
+    private void addProvidersLocked(AndroidPackage pkg, boolean chatty) {
+        final int providersSize = ArrayUtils.size(pkg.getProviders());
         StringBuilder r = null;
         for (int i = 0; i < providersSize; i++) {
-            PackageParser.Provider p = pkg.providers.get(i);
-            p.info.processName = fixProcessName(pkg.applicationInfo.processName,
-                    p.info.processName);
+            EffectiveProvider p = new EffectiveProvider(pkg.getProviders().get(i));
             mProviders.addProvider(p);
-            p.syncable = p.info.isSyncable;
-            if (p.info.authority != null) {
-                String[] names = p.info.authority.split(";");
-                p.info.authority = null;
+            if (p.getAuthority() != null) {
+                String[] names = p.getAuthority().split(";");
+                p.setEffectiveAuthority(null);
                 for (int j = 0; j < names.length; j++) {
-                    if (j == 1 && p.syncable) {
+                    if (j == 1 && p.isSyncable()) {
                         // We only want the first authority for a provider to possibly be
                         // syncable, so if we already added this provider using a different
                         // authority clear the syncable flag. We copy the provider before
@@ -574,23 +624,23 @@
                         // to a provider that we don't want to change.
                         // Only do this for the second authority since the resulting provider
                         // object can be the same for all future authorities for this provider.
-                        p = new PackageParser.Provider(p);
-                        p.syncable = false;
+                        p = new EffectiveProvider(p);
+                        p.setEffectiveSyncable(false);
                     }
                     if (!mProvidersByAuthority.containsKey(names[j])) {
                         mProvidersByAuthority.put(names[j], p);
-                        if (p.info.authority == null) {
-                            p.info.authority = names[j];
+                        if (p.getAuthority() == null) {
+                            p.setEffectiveAuthority(names[j]);
                         } else {
-                            p.info.authority = p.info.authority + ";" + names[j];
+                            p.setEffectiveAuthority(p.getAuthority() + ";" + names[j]);
                         }
                         if (DEBUG_PACKAGE_SCANNING && chatty) {
                             Log.d(TAG, "Registered content provider: " + names[j]
-                                    + ", className = " + p.info.name
-                                    + ", isSyncable = " + p.info.isSyncable);
+                                    + ", className = " + p.getName()
+                                    + ", isSyncable = " + p.isSyncable());
                         }
                     } else {
-                        final PackageParser.Provider other =
+                        final ParsedProvider other =
                                 mProvidersByAuthority.get(names[j]);
                         final ComponentName component =
                                 (other != null && other.getComponentName() != null)
@@ -598,7 +648,7 @@
                         final String packageName =
                                 component != null ? component.getPackageName() : "?";
                         Slog.w(TAG, "Skipping provider name " + names[j]
-                                + " (in package " + pkg.applicationInfo.packageName + ")"
+                                + " (in package " + pkg.getAppInfoPackageName() + ")"
                                 + ": name already used by " + packageName);
                     }
                 }
@@ -609,7 +659,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(p.info.name);
+                r.append(p.getName());
             }
         }
         if (DEBUG_PACKAGE_SCANNING && chatty) {
@@ -618,13 +668,11 @@
     }
 
     @GuardedBy("mLock")
-    private void addReceiversLocked(PackageParser.Package pkg, boolean chatty) {
-        final int receiversSize = pkg.receivers.size();
+    private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
+        final int receiversSize = ArrayUtils.size(pkg.getReceivers());
         StringBuilder r = null;
         for (int i = 0; i < receiversSize; i++) {
-            PackageParser.Activity a = pkg.receivers.get(i);
-            a.info.processName = fixProcessName(pkg.applicationInfo.processName,
-                    a.info.processName);
+            ParsedActivity a = pkg.getReceivers().get(i);
             mReceivers.addActivity(a, "receiver", null);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
@@ -632,7 +680,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(a.info.name);
+                r.append(a.getName());
             }
         }
         if (DEBUG_PACKAGE_SCANNING && chatty) {
@@ -641,13 +689,11 @@
     }
 
     @GuardedBy("mLock")
-    private void addServicesLocked(PackageParser.Package pkg, boolean chatty) {
-        final int servicesSize = pkg.services.size();
+    private void addServicesLocked(AndroidPackage pkg, boolean chatty) {
+        final int servicesSize = ArrayUtils.size(pkg.getServices());
         StringBuilder r = null;
         for (int i = 0; i < servicesSize; i++) {
-            PackageParser.Service s = pkg.services.get(i);
-            s.info.processName = fixProcessName(pkg.applicationInfo.processName,
-                    s.info.processName);
+            ParsedService s = pkg.getServices().get(i);
             mServices.addService(s);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
@@ -655,7 +701,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(s.info.name);
+                r.append(s.getName());
             }
         }
         if (DEBUG_PACKAGE_SCANNING && chatty) {
@@ -663,13 +709,12 @@
         }
     }
 
-
     /**
      * <em>WARNING</em> for performance reasons, the passed in intentList WILL BE
      * MODIFIED. Do not pass in a list that should not be changed.
      */
-    private static <T> void getIntentListSubset(List<ActivityIntentInfo> intentList,
-            IterGenerator<T> generator, Iterator<T> searchIterator) {
+    private static <T> void getIntentListSubset(List<ParsedActivityIntentInfo> intentList,
+            Function<ParsedActivityIntentInfo, Iterator<T>> generator, Iterator<T> searchIterator) {
         // loop through the set of actions; every one must be found in the intent filter
         while (searchIterator.hasNext()) {
             // we must have at least one filter in the list to consider a match
@@ -680,14 +725,14 @@
             final T searchAction = searchIterator.next();
 
             // loop through the set of intent filters
-            final Iterator<ActivityIntentInfo> intentIter = intentList.iterator();
+            final Iterator<ParsedActivityIntentInfo> intentIter = intentList.iterator();
             while (intentIter.hasNext()) {
-                final ActivityIntentInfo intentInfo = intentIter.next();
+                final ParsedActivityIntentInfo intentInfo = intentIter.next();
                 boolean selectionFound = false;
 
                 // loop through the intent filter's selection criteria; at least one
                 // of them must match the searched criteria
-                final Iterator<T> intentSelectionIter = generator.generate(intentInfo);
+                final Iterator<T> intentSelectionIter = generator.apply(intentInfo);
                 while (intentSelectionIter != null && intentSelectionIter.hasNext()) {
                     final T intentSelection = intentSelectionIter.next();
                     if (intentSelection != null && intentSelection.equals(searchAction)) {
@@ -705,7 +750,7 @@
         }
     }
 
-    private static boolean isProtectedAction(ActivityIntentInfo filter) {
+    private static boolean isProtectedAction(ParsedActivityIntentInfo filter) {
         final Iterator<String> actionsIter = filter.actionsIterator();
         while (actionsIter != null && actionsIter.hasNext()) {
             final String filterAction = actionsIter.next();
@@ -719,20 +764,20 @@
     /**
      * Finds a privileged activity that matches the specified activity names.
      */
-    private static PackageParser.Activity findMatchingActivity(
-            List<PackageParser.Activity> activityList, ActivityInfo activityInfo) {
-        for (PackageParser.Activity sysActivity : activityList) {
-            if (sysActivity.info.name.equals(activityInfo.name)) {
+    private static ParsedActivity findMatchingActivity(
+            List<ParsedActivity> activityList, ParsedActivity activityInfo) {
+        for (ParsedActivity sysActivity : activityList) {
+            if (sysActivity.getName().equals(activityInfo.getName())) {
                 return sysActivity;
             }
-            if (sysActivity.info.name.equals(activityInfo.targetActivity)) {
+            if (sysActivity.getName().equals(activityInfo.targetActivity)) {
                 return sysActivity;
             }
-            if (sysActivity.info.targetActivity != null) {
-                if (sysActivity.info.targetActivity.equals(activityInfo.name)) {
+            if (sysActivity.targetActivity != null) {
+                if (sysActivity.targetActivity.equals(activityInfo.getName())) {
                     return sysActivity;
                 }
-                if (sysActivity.info.targetActivity.equals(activityInfo.targetActivity)) {
+                if (sysActivity.targetActivity.equals(activityInfo.targetActivity)) {
                     return sysActivity;
                 }
             }
@@ -753,24 +798,23 @@
      * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
      * allowed to obtain any priority on any action.
      */
-    private void adjustPriority(List<PackageParser.Activity> systemActivities,
-            ActivityIntentInfo intent, String setupWizardPackage) {
+    private void adjustPriority(List<ParsedActivity> systemActivities,
+            ParsedActivityIntentInfo intent, String setupWizardPackage) {
         // nothing to do; priority is fine as-is
         if (intent.getPriority() <= 0) {
             return;
         }
 
-        final ActivityInfo activityInfo = intent.activity.info;
-        final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
+        AndroidPackage pkg = sPackageManagerInternal.getPackage(intent.getPackageName());
 
         final boolean privilegedApp =
-                ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
+                ((pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
         if (!privilegedApp) {
             // non-privileged applications can never define a priority >0
             if (DEBUG_FILTERS) {
                 Slog.i(TAG, "Non-privileged app; cap priority to 0;"
-                        + " package: " + applicationInfo.packageName
-                        + " activity: " + intent.activity.className
+                        + " package: " + pkg.getPackageName()
+                        + " activity: " + intent.getClassName()
                         + " origPrio: " + intent.getPriority());
             }
             intent.setPriority(0);
@@ -794,8 +838,8 @@
                     mProtectedFilters.add(intent);
                     if (DEBUG_FILTERS) {
                         Slog.i(TAG, "Protected action; save for later;"
-                                + " package: " + applicationInfo.packageName
-                                + " activity: " + intent.activity.className
+                                + " package: " + pkg.getPackageName()
+                                + " activity: " + intent.getClassName()
                                 + " origPrio: " + intent.getPriority());
                     }
                     return;
@@ -804,12 +848,12 @@
                         Slog.i(TAG, "No setup wizard;"
                                 + " All protected intents capped to priority 0");
                     }
-                    if (intent.activity.info.packageName.equals(setupWizardPackage)) {
+                    if (intent.getPackageName().equals(setupWizardPackage)) {
                         if (DEBUG_FILTERS) {
                             Slog.i(TAG, "Found setup wizard;"
                                     + " allow priority " + intent.getPriority() + ";"
-                                    + " package: " + intent.activity.info.packageName
-                                    + " activity: " + intent.activity.className
+                                    + " package: " + intent.getPackageName()
+                                    + " activity: " + intent.getClassName()
                                     + " priority: " + intent.getPriority());
                         }
                         // setup wizard gets whatever it wants
@@ -817,8 +861,8 @@
                     }
                     if (DEBUG_FILTERS) {
                         Slog.i(TAG, "Protected action; cap priority to 0;"
-                                + " package: " + intent.activity.info.packageName
-                                + " activity: " + intent.activity.className
+                                + " package: " + intent.getPackageName()
+                                + " activity: " + intent.getClassName()
                                 + " origPrio: " + intent.getPriority());
                     }
                     intent.setPriority(0);
@@ -830,14 +874,28 @@
         }
 
         // privileged app unbundled update ... try to find the same activity
-        final PackageParser.Activity foundActivity =
-                findMatchingActivity(systemActivities, activityInfo);
+
+        ParsedActivity foundActivity = null;
+        ParsedActivity activity = null;
+
+        if (pkg.getActivities() != null) {
+            for (ParsedActivity parsedProvider : pkg.getActivities()) {
+                if (Objects.equals(parsedProvider.className, intent.getClassName())) {
+                    activity = parsedProvider;
+                }
+            }
+        }
+
+        if (activity != null) {
+            foundActivity = findMatchingActivity(systemActivities, activity);
+        }
+
         if (foundActivity == null) {
             // this is a new activity; it cannot obtain >0 priority
             if (DEBUG_FILTERS) {
                 Slog.i(TAG, "New activity; cap priority to 0;"
-                        + " package: " + applicationInfo.packageName
-                        + " activity: " + intent.activity.className
+                        + " package: " + pkg.getPackageName()
+                        + " activity: " + intent.getClassName()
                         + " origPrio: " + intent.getPriority());
             }
             intent.setPriority(0);
@@ -847,19 +905,19 @@
         // found activity, now check for filter equivalence
 
         // a shallow copy is enough; we modify the list, not its contents
-        final List<ActivityIntentInfo> intentListCopy = new ArrayList<>(foundActivity.intents);
-        final List<ActivityIntentInfo> foundFilters = mActivities.findFilters(intent);
+        final List<ParsedActivityIntentInfo> intentListCopy =
+                new ArrayList<>(foundActivity.intents);
 
         // find matching action subsets
         final Iterator<String> actionsIterator = intent.actionsIterator();
         if (actionsIterator != null) {
-            getIntentListSubset(intentListCopy, new ActionIterGenerator(), actionsIterator);
+            getIntentListSubset(intentListCopy, IntentFilter::actionsIterator, actionsIterator);
             if (intentListCopy.size() == 0) {
                 // no more intents to match; we're not equivalent
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Mismatched action; cap priority to 0;"
-                            + " package: " + applicationInfo.packageName
-                            + " activity: " + intent.activity.className
+                            + " package: " + pkg.getPackageName()
+                            + " activity: " + intent.getClassName()
                             + " origPrio: " + intent.getPriority());
                 }
                 intent.setPriority(0);
@@ -870,13 +928,14 @@
         // find matching category subsets
         final Iterator<String> categoriesIterator = intent.categoriesIterator();
         if (categoriesIterator != null) {
-            getIntentListSubset(intentListCopy, new CategoriesIterGenerator(), categoriesIterator);
+            getIntentListSubset(intentListCopy, IntentFilter::categoriesIterator,
+                    categoriesIterator);
             if (intentListCopy.size() == 0) {
                 // no more intents to match; we're not equivalent
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Mismatched category; cap priority to 0;"
-                            + " package: " + applicationInfo.packageName
-                            + " activity: " + intent.activity.className
+                            + " package: " + pkg.getPackageName()
+                            + " activity: " + intent.getClassName()
                             + " origPrio: " + intent.getPriority());
                 }
                 intent.setPriority(0);
@@ -887,13 +946,13 @@
         // find matching schemes subsets
         final Iterator<String> schemesIterator = intent.schemesIterator();
         if (schemesIterator != null) {
-            getIntentListSubset(intentListCopy, new SchemesIterGenerator(), schemesIterator);
+            getIntentListSubset(intentListCopy, IntentFilter::schemesIterator, schemesIterator);
             if (intentListCopy.size() == 0) {
                 // no more intents to match; we're not equivalent
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
-                            + " package: " + applicationInfo.packageName
-                            + " activity: " + intent.activity.className
+                            + " package: " + pkg.getPackageName()
+                            + " activity: " + intent.getClassName()
                             + " origPrio: " + intent.getPriority());
                 }
                 intent.setPriority(0);
@@ -905,14 +964,14 @@
         final Iterator<IntentFilter.AuthorityEntry> authoritiesIterator =
                 intent.authoritiesIterator();
         if (authoritiesIterator != null) {
-            getIntentListSubset(intentListCopy, new AuthoritiesIterGenerator(),
+            getIntentListSubset(intentListCopy, IntentFilter::authoritiesIterator,
                     authoritiesIterator);
             if (intentListCopy.size() == 0) {
                 // no more intents to match; we're not equivalent
                 if (DEBUG_FILTERS) {
                     Slog.i(TAG, "Mismatched authority; cap priority to 0;"
-                            + " package: " + applicationInfo.packageName
-                            + " activity: " + intent.activity.className
+                            + " package: " + pkg.getPackageName()
+                            + " activity: " + intent.getClassName()
                             + " origPrio: " + intent.getPriority());
                 }
                 intent.setPriority(0);
@@ -929,8 +988,8 @@
             if (DEBUG_FILTERS) {
                 Slog.i(TAG, "Found matching filter(s);"
                         + " cap priority to " + cappedPriority + ";"
-                        + " package: " + applicationInfo.packageName
-                        + " activity: " + intent.activity.className
+                        + " package: " + pkg.getPackageName()
+                        + " activity: " + intent.getClassName()
                         + " origPrio: " + intent.getPriority());
             }
             intent.setPriority(cappedPriority);
@@ -940,15 +999,15 @@
     }
 
     @GuardedBy("mLock")
-    private void removeAllComponentsLocked(PackageParser.Package pkg, boolean chatty) {
+    private void removeAllComponentsLocked(AndroidPackage pkg, boolean chatty) {
         int componentSize;
         StringBuilder r;
         int i;
 
-        componentSize = pkg.activities.size();
+        componentSize = ArrayUtils.size(pkg.getActivities());
         r = null;
         for (i = 0; i < componentSize; i++) {
-            PackageParser.Activity a = pkg.activities.get(i);
+            ParsedActivity a = pkg.getActivities().get(i);
             mActivities.removeActivity(a, "activity");
             if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
@@ -956,32 +1015,32 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(a.info.name);
+                r.append(a.getName());
             }
         }
         if (DEBUG_REMOVE && chatty) {
             Log.d(TAG, "  Activities: " + (r == null ? "<NONE>" : r));
         }
 
-        componentSize = pkg.providers.size();
+        componentSize = ArrayUtils.size(pkg.getProviders());
         r = null;
         for (i = 0; i < componentSize; i++) {
-            PackageParser.Provider p = pkg.providers.get(i);
+            ParsedProvider p = pkg.getProviders().get(i);
             mProviders.removeProvider(p);
-            if (p.info.authority == null) {
+            if (p.getAuthority() == null) {
                 // Another content provider with this authority existed when this app was
                 // installed, so this authority is null. Ignore it as we don't have to
                 // unregister the provider.
                 continue;
             }
-            String[] names = p.info.authority.split(";");
+            String[] names = p.getAuthority().split(";");
             for (int j = 0; j < names.length; j++) {
                 if (mProvidersByAuthority.get(names[j]) == p) {
                     mProvidersByAuthority.remove(names[j]);
                     if (DEBUG_REMOVE && chatty) {
                         Log.d(TAG, "Unregistered content provider: " + names[j]
-                                + ", className = " + p.info.name + ", isSyncable = "
-                                + p.info.isSyncable);
+                                + ", className = " + p.getName() + ", isSyncable = "
+                                + p.isSyncable());
                     }
                 }
             }
@@ -991,17 +1050,17 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(p.info.name);
+                r.append(p.getName());
             }
         }
         if (DEBUG_REMOVE && chatty) {
             Log.d(TAG, "  Providers: " + (r == null ? "<NONE>" : r));
         }
 
-        componentSize = pkg.receivers.size();
+        componentSize = ArrayUtils.size(pkg.getReceivers());
         r = null;
         for (i = 0; i < componentSize; i++) {
-            PackageParser.Activity a = pkg.receivers.get(i);
+            ParsedActivity a = pkg.getReceivers().get(i);
             mReceivers.removeActivity(a, "receiver");
             if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
@@ -1009,17 +1068,17 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(a.info.name);
+                r.append(a.getName());
             }
         }
         if (DEBUG_REMOVE && chatty) {
             Log.d(TAG, "  Receivers: " + (r == null ? "<NONE>" : r));
         }
 
-        componentSize = pkg.services.size();
+        componentSize = ArrayUtils.size(pkg.getServices());
         r = null;
         for (i = 0; i < componentSize; i++) {
-            PackageParser.Service s = pkg.services.get(i);
+            ParsedService s = pkg.getServices().get(i);
             mServices.removeService(s);
             if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
@@ -1027,7 +1086,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(s.info.name);
+                r.append(s.getName());
             }
         }
         if (DEBUG_REMOVE && chatty) {
@@ -1036,26 +1095,26 @@
     }
 
     @GuardedBy("mLock")
-    private void assertProvidersNotDefinedLocked(PackageParser.Package pkg)
+    private void assertProvidersNotDefinedLocked(AndroidPackage pkg)
             throws PackageManagerException {
-        final int providersSize = pkg.providers.size();
+        final int providersSize = ArrayUtils.size(pkg.getProviders());
         int i;
         for (i = 0; i < providersSize; i++) {
-            PackageParser.Provider p = pkg.providers.get(i);
-            if (p.info.authority != null) {
-                final String[] names = p.info.authority.split(";");
+            ParsedProvider p = pkg.getProviders().get(i);
+            if (p.getAuthority() != null) {
+                final String[] names = p.getAuthority().split(";");
                 for (int j = 0; j < names.length; j++) {
                     if (mProvidersByAuthority.containsKey(names[j])) {
-                        final PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
+                        final ParsedProvider other = mProvidersByAuthority.get(names[j]);
                         final String otherPackageName =
                                 (other != null && other.getComponentName() != null)
                                         ? other.getComponentName().getPackageName() : "?";
                         // if we're installing over the same already-installed package, this is ok
-                        if (!otherPackageName.equals(pkg.packageName)) {
+                        if (!otherPackageName.equals(pkg.getPackageName())) {
                             throw new PackageManagerException(
                                     INSTALL_FAILED_CONFLICTING_PROVIDER,
                                     "Can't install because provider name " + names[j]
-                                            + " (in package " + pkg.applicationInfo.packageName
+                                            + " (in package " + pkg.getPackageName()
                                             + ") is already used by " + otherPackageName);
                         }
                     }
@@ -1064,8 +1123,9 @@
         }
     }
 
-    private static final class ActivityIntentResolver
-            extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
+    private static class ActivityIntentResolver
+            extends IntentResolver<ParsedActivityIntentInfo, ResolveInfo> {
+
         @Override
         public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
                 boolean defaultOnly, int userId) {
@@ -1086,24 +1146,24 @@
         }
 
         List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                int flags, List<PackageParser.Activity> packageActivities, int userId) {
+                int flags, List<ParsedActivity> packageActivities, int userId) {
             if (!sUserManager.exists(userId)) {
                 return null;
             }
             if (packageActivities == null) {
-                return null;
+                return Collections.emptyList();
             }
             mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int activitiesSize = packageActivities.size();
-            ArrayList<PackageParser.ActivityIntentInfo[]> listCut = new ArrayList<>(activitiesSize);
+            ArrayList<ParsedActivityIntentInfo[]> listCut = new ArrayList<>(activitiesSize);
 
-            ArrayList<PackageParser.ActivityIntentInfo> intentFilters;
+            List<ParsedActivityIntentInfo> intentFilters;
             for (int i = 0; i < activitiesSize; ++i) {
                 intentFilters = packageActivities.get(i).intents;
                 if (intentFilters != null && intentFilters.size() > 0) {
-                    PackageParser.ActivityIntentInfo[] array =
-                            new PackageParser.ActivityIntentInfo[intentFilters.size()];
+                    ParsedActivityIntentInfo[] array =
+                            new ParsedActivityIntentInfo[intentFilters.size()];
                     intentFilters.toArray(array);
                     listCut.add(array);
                 }
@@ -1111,21 +1171,21 @@
             return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
         }
 
-        private void addActivity(PackageParser.Activity a, String type,
-                List<PackageParser.ActivityIntentInfo> newIntents) {
+        private void addActivity(ParsedActivity a, String type,
+                List<ParsedActivityIntentInfo> newIntents) {
             mActivities.put(a.getComponentName(), a);
             if (DEBUG_SHOW_INFO) {
-                final CharSequence label = a.info.nonLocalizedLabel != null
-                        ? a.info.nonLocalizedLabel
-                        : a.info.name;
+                final CharSequence label = a.nonLocalizedLabel != null
+                        ? a.nonLocalizedLabel
+                        : a.getName();
                 Log.v(TAG, "  " + type + " " + label + ":");
             }
             if (DEBUG_SHOW_INFO) {
-                Log.v(TAG, "    Class=" + a.info.name);
+                Log.v(TAG, "    Class=" + a.getName());
             }
             final int intentsSize = a.intents.size();
             for (int j = 0; j < intentsSize; j++) {
-                PackageParser.ActivityIntentInfo intent = a.intents.get(j);
+                ParsedActivityIntentInfo intent = a.intents.get(j);
                 if (newIntents != null && "activity".equals(type)) {
                     newIntents.add(intent);
                 }
@@ -1134,23 +1194,23 @@
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 if (!intent.debugCheck()) {
-                    Log.w(TAG, "==> For Activity " + a.info.name);
+                    Log.w(TAG, "==> For Activity " + a.getName());
                 }
                 addFilter(intent);
             }
         }
 
-        private void removeActivity(PackageParser.Activity a, String type) {
+        private void removeActivity(ParsedActivity a, String type) {
             mActivities.remove(a.getComponentName());
             if (DEBUG_SHOW_INFO) {
                 Log.v(TAG, "  " + type + " "
-                        + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel
-                                : a.info.name) + ":");
-                Log.v(TAG, "    Class=" + a.info.name);
+                        + (a.nonLocalizedLabel != null ? a.nonLocalizedLabel
+                                : a.getName()) + ":");
+                Log.v(TAG, "    Class=" + a.getName());
             }
             final int intentsSize = a.intents.size();
             for (int j = 0; j < intentsSize; j++) {
-                PackageParser.ActivityIntentInfo intent = a.intents.get(j);
+                ParsedActivityIntentInfo intent = a.intents.get(j);
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
@@ -1161,11 +1221,11 @@
 
         @Override
         protected boolean allowFilterResult(
-                PackageParser.ActivityIntentInfo filter, List<ResolveInfo> dest) {
-            ActivityInfo filterAi = filter.activity.info;
+                ParsedActivityIntentInfo filter, List<ResolveInfo> dest) {
             for (int i = dest.size() - 1; i >= 0; --i) {
                 ActivityInfo destAi = dest.get(i).activityInfo;
-                if (destAi.name == filterAi.name && destAi.packageName == filterAi.packageName) {
+                if (Objects.equals(destAi.name, filter.getClassName())
+                        && Objects.equals(destAi.packageName, filter.getPackageName())) {
                     return false;
                 }
             }
@@ -1173,34 +1233,39 @@
         }
 
         @Override
-        protected ActivityIntentInfo[] newArray(int size) {
-            return new ActivityIntentInfo[size];
+        protected ParsedActivityIntentInfo[] newArray(int size) {
+            return new ParsedActivityIntentInfo[size];
         }
 
         @Override
-        protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
+        protected boolean isFilterStopped(ParsedActivityIntentInfo filter, int userId) {
             if (!sUserManager.exists(userId)) return true;
-            PackageParser.Package p = filter.activity.owner;
-            if (p != null) {
-                PackageSetting ps = (PackageSetting) p.mExtras;
-                if (ps != null) {
-                    // System apps are never considered stopped for purposes of
-                    // filtering, because there may be no way for the user to
-                    // actually re-launch them.
-                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
-                            && ps.getStopped(userId);
-                }
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg == null) {
+                return false;
             }
-            return false;
+
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    filter.getPackageName());
+            if (ps == null) {
+                return false;
+            }
+
+            // System apps are never considered stopped for purposes of
+            // filtering, because there may be no way for the user to
+            // actually re-launch them.
+            return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && ps.getStopped(userId);
         }
 
         @Override
         protected boolean isPackageForFilter(String packageName,
-                PackageParser.ActivityIntentInfo info) {
-            return packageName.equals(info.activity.owner.packageName);
+                ParsedActivityIntentInfo info) {
+            return packageName.equals(info.getPackageName());
         }
 
-        private void log(String reason, ActivityIntentInfo info, int match,
+        private void log(String reason, ParsedActivityIntentInfo info, int match,
                 int userId) {
             Slog.w(TAG, reason
                     + "; match: "
@@ -1210,7 +1275,7 @@
         }
 
         @Override
-        protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
+        protected ResolveInfo newResult(ParsedActivityIntentInfo info,
                 int match, int userId) {
             if (!sUserManager.exists(userId)) {
                 if (DEBUG) {
@@ -1218,7 +1283,29 @@
                 }
                 return null;
             }
-            if (!sPackageManagerInternal.isEnabledAndMatches(info.activity.info, mFlags, userId)) {
+
+            ParsedActivity activity = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(info.getPackageName());
+            if (pkg == null) {
+                return null;
+            }
+
+            // TODO(b/135203078): Consider more efficient ways of doing this.
+            List<ParsedActivity> activities = getResolveList(pkg);
+            if (activities != null) {
+                for (ParsedActivity parsedActivity : activities) {
+                    if (Objects.equals(parsedActivity.className, info.getClassName())) {
+                        activity = parsedActivity;
+                    }
+                }
+            }
+
+            if (activity == null) {
+                return null;
+            }
+
+            if (!sPackageManagerInternal.isEnabledAndMatches(activity, mFlags, userId)) {
                 if (DEBUG) {
                     log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
                             + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
@@ -1226,8 +1313,8 @@
                 }
                 return null;
             }
-            final PackageParser.Activity activity = info.activity;
-            PackageSetting ps = (PackageSetting) activity.owner.mExtras;
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    info.getPackageName());
             if (ps == null) {
                 if (DEBUG) {
                     log("info.activity.owner.mExtras == null", info, match, userId);
@@ -1236,10 +1323,10 @@
             }
             final PackageUserState userState = ps.readUserState(userId);
             ActivityInfo ai =
-                    PackageParser.generateActivityInfo(activity, mFlags, userState, userId);
+                    PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags, userState, userId);
             if (ai == null) {
                 if (DEBUG) {
-                    log("Failed to create ActivityInfo based on " + info.activity, info, match,
+                    log("Failed to create ActivityInfo based on " + activity, info, match,
                             userId);
                 }
                 return null;
@@ -1289,7 +1376,7 @@
             }
             res.handleAllWebDataURI = info.handleAllWebDataURI();
             res.priority = info.getPriority();
-            res.preferredOrder = activity.owner.mPreferredOrder;
+            res.preferredOrder = pkg.getPreferredOrder();
             //System.out.println("Result: " + res.activityInfo.className +
             //                   " = " + res.priority);
             res.match = match;
@@ -1314,40 +1401,64 @@
 
         @Override
         protected void dumpFilter(PrintWriter out, String prefix,
-                PackageParser.ActivityIntentInfo filter) {
+                ParsedActivityIntentInfo filter) {
+            ParsedActivity activity = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg != null && pkg.getActivities() != null) {
+                for (ParsedActivity parsedActivity : pkg.getActivities()) {
+                    if (Objects.equals(parsedActivity.className, filter.getClassName())) {
+                        activity = parsedActivity;
+                    }
+                }
+            }
+
             out.print(prefix);
-            out.print(Integer.toHexString(System.identityHashCode(filter.activity)));
+            out.print(Integer.toHexString(System.identityHashCode(activity)));
             out.print(' ');
-            filter.activity.printComponentShortName(out);
+            ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
             out.print(" filter ");
             out.println(Integer.toHexString(System.identityHashCode(filter)));
         }
 
         @Override
-        protected Object filterToLabel(PackageParser.ActivityIntentInfo filter) {
-            return filter.activity;
+        protected Object filterToLabel(ParsedActivityIntentInfo filter) {
+            return filter;
         }
 
         protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
-            PackageParser.Activity activity = (PackageParser.Activity) label;
+            ParsedActivityIntentInfo activity = (ParsedActivityIntentInfo) label;
             out.print(prefix);
             out.print(Integer.toHexString(System.identityHashCode(activity)));
             out.print(' ');
-            activity.printComponentShortName(out);
+            ComponentName.printShortString(out, activity.getPackageName(), activity.getClassName());
             if (count > 1) {
                 out.print(" ("); out.print(count); out.print(" filters)");
             }
             out.println();
         }
 
+        protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
+            return pkg.getActivities();
+        }
+
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, PackageParser.Activity> mActivities =
+        private final ArrayMap<ComponentName, ParsedActivity> mActivities =
                 new ArrayMap<>();
         private int mFlags;
     }
 
+    // Both receivers and activities share a class, but point to different get methods
+    private static final class ReceiverIntentResolver extends ActivityIntentResolver {
+
+        @Override
+        protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
+            return pkg.getReceivers();
+        }
+    }
+
     private static final class ProviderIntentResolver
-            extends IntentResolver<PackageParser.ProviderIntentInfo, ResolveInfo> {
+            extends IntentResolver<ParsedProviderIntentInfo, ResolveInfo> {
         @Override
         public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
                 boolean defaultOnly, int userId) {
@@ -1367,24 +1478,24 @@
         }
 
         List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                int flags, List<PackageParser.Provider> packageProviders, int userId) {
+                int flags, List<ParsedProvider> packageProviders, int userId) {
             if (!sUserManager.exists(userId)) {
                 return null;
             }
             if (packageProviders == null) {
-                return null;
+                return Collections.emptyList();
             }
             mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int providersSize = packageProviders.size();
-            ArrayList<PackageParser.ProviderIntentInfo[]> listCut = new ArrayList<>(providersSize);
+            ArrayList<ParsedProviderIntentInfo[]> listCut = new ArrayList<>(providersSize);
 
-            ArrayList<PackageParser.ProviderIntentInfo> intentFilters;
+            List<ParsedProviderIntentInfo> intentFilters;
             for (int i = 0; i < providersSize; ++i) {
-                intentFilters = packageProviders.get(i).intents;
+                intentFilters = packageProviders.get(i).getIntents();
                 if (intentFilters != null && intentFilters.size() > 0) {
-                    PackageParser.ProviderIntentInfo[] array =
-                            new PackageParser.ProviderIntentInfo[intentFilters.size()];
+                    ParsedProviderIntentInfo[] array =
+                            new ParsedProviderIntentInfo[intentFilters.size()];
                     intentFilters.toArray(array);
                     listCut.add(array);
                 }
@@ -1392,7 +1503,7 @@
             return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
         }
 
-        void addProvider(PackageParser.Provider p) {
+        void addProvider(ParsedProvider p) {
             if (mProviders.containsKey(p.getComponentName())) {
                 Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
                 return;
@@ -1401,39 +1512,39 @@
             mProviders.put(p.getComponentName(), p);
             if (DEBUG_SHOW_INFO) {
                 Log.v(TAG, "  "
-                        + (p.info.nonLocalizedLabel != null
-                                ? p.info.nonLocalizedLabel
-                                : p.info.name)
+                        + (p.nonLocalizedLabel != null
+                                ? p.nonLocalizedLabel
+                                : p.getName())
                         + ":");
-                Log.v(TAG, "    Class=" + p.info.name);
+                Log.v(TAG, "    Class=" + p.getName());
             }
-            final int intentsSize = p.intents.size();
+            final int intentsSize = p.getIntents().size();
             int j;
             for (j = 0; j < intentsSize; j++) {
-                PackageParser.ProviderIntentInfo intent = p.intents.get(j);
+                ParsedProviderIntentInfo intent = p.getIntents().get(j);
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 if (!intent.debugCheck()) {
-                    Log.w(TAG, "==> For Provider " + p.info.name);
+                    Log.w(TAG, "==> For Provider " + p.getName());
                 }
                 addFilter(intent);
             }
         }
 
-        void removeProvider(PackageParser.Provider p) {
+        void removeProvider(ParsedProvider p) {
             mProviders.remove(p.getComponentName());
             if (DEBUG_SHOW_INFO) {
-                Log.v(TAG, "  " + (p.info.nonLocalizedLabel != null
-                        ? p.info.nonLocalizedLabel
-                        : p.info.name) + ":");
-                Log.v(TAG, "    Class=" + p.info.name);
+                Log.v(TAG, "  " + (p.nonLocalizedLabel != null
+                        ? p.nonLocalizedLabel
+                        : p.getName()) + ":");
+                Log.v(TAG, "    Class=" + p.getName());
             }
-            final int intentsSize = p.intents.size();
+            final int intentsSize = p.getIntents().size();
             int j;
             for (j = 0; j < intentsSize; j++) {
-                PackageParser.ProviderIntentInfo intent = p.intents.get(j);
+                ParsedProviderIntentInfo intent = p.getIntents().get(j);
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
@@ -1444,12 +1555,11 @@
 
         @Override
         protected boolean allowFilterResult(
-                PackageParser.ProviderIntentInfo filter, List<ResolveInfo> dest) {
-            ProviderInfo filterPi = filter.provider.info;
+                ParsedProviderIntentInfo filter, List<ResolveInfo> dest) {
             for (int i = dest.size() - 1; i >= 0; i--) {
                 ProviderInfo destPi = dest.get(i).providerInfo;
-                if (destPi.name == filterPi.name
-                        && destPi.packageName == filterPi.packageName) {
+                if (Objects.equals(destPi.name, filter.getClassName())
+                        && Objects.equals(destPi.packageName, filter.getPackageName())) {
                     return false;
                 }
             }
@@ -1457,47 +1567,68 @@
         }
 
         @Override
-        protected PackageParser.ProviderIntentInfo[] newArray(int size) {
-            return new PackageParser.ProviderIntentInfo[size];
+        protected ParsedProviderIntentInfo[] newArray(int size) {
+            return new ParsedProviderIntentInfo[size];
         }
 
         @Override
-        protected boolean isFilterStopped(PackageParser.ProviderIntentInfo filter, int userId) {
+        protected boolean isFilterStopped(ParsedProviderIntentInfo filter, int userId) {
             if (!sUserManager.exists(userId)) {
                 return true;
             }
-            PackageParser.Package p = filter.provider.owner;
-            if (p != null) {
-                PackageSetting ps = (PackageSetting) p.mExtras;
-                if (ps != null) {
-                    // System apps are never considered stopped for purposes of
-                    // filtering, because there may be no way for the user to
-                    // actually re-launch them.
-                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
-                            && ps.getStopped(userId);
-                }
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg == null) {
+                return false;
             }
-            return false;
+
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    filter.getPackageName());
+            if (ps == null) {
+                return false;
+            }
+
+            // System apps are never considered stopped for purposes of
+            // filtering, because there may be no way for the user to
+            // actually re-launch them.
+            return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && ps.getStopped(userId);
         }
 
         @Override
         protected boolean isPackageForFilter(String packageName,
-                PackageParser.ProviderIntentInfo info) {
-            return packageName.equals(info.provider.owner.packageName);
+                ParsedProviderIntentInfo info) {
+            return packageName.equals(info.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(PackageParser.ProviderIntentInfo filter,
+        protected ResolveInfo newResult(ParsedProviderIntentInfo filter,
                 int match, int userId) {
             if (!sUserManager.exists(userId)) {
                 return null;
             }
-            final PackageParser.ProviderIntentInfo info = filter;
-            if (!sPackageManagerInternal.isEnabledAndMatches(info.provider.info, mFlags, userId)) {
+
+            ParsedProvider provider = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg != null && pkg.getProviders() != null) {
+                for (ParsedProvider parsedProvider : pkg.getProviders()) {
+                    if (Objects.equals(parsedProvider.className, filter.getClassName())) {
+                        provider = parsedProvider;
+                    }
+                }
+            }
+
+            if (provider == null) {
                 return null;
             }
-            final PackageParser.Provider provider = info.provider;
-            PackageSetting ps = (PackageSetting) provider.owner.mExtras;
+
+            if (!sPackageManagerInternal.isEnabledAndMatches(provider, mFlags, userId)) {
+                return null;
+            }
+
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    filter.getPackageName());
             if (ps == null) {
                 return null;
             }
@@ -1507,7 +1638,7 @@
             final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to instant applications
             if (matchVisibleToInstantApp
-                    && !(info.isVisibleToInstantApp() || userState.instantApp)) {
+                    && !(filter.isVisibleToInstantApp() || userState.instantApp)) {
                 return null;
             }
             // throw out instant application filters if we're not explicitly requesting them
@@ -1519,8 +1650,8 @@
             if (userState.instantApp && ps.isUpdateAvailable()) {
                 return null;
             }
-            ProviderInfo pi = PackageParser.generateProviderInfo(provider, mFlags,
-                    userState, userId);
+            ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider,
+                    mFlags, userState, userId);
             if (pi == null) {
                 return null;
             }
@@ -1529,13 +1660,13 @@
             if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
-            res.priority = info.getPriority();
-            res.preferredOrder = provider.owner.mPreferredOrder;
+            res.priority = filter.getPriority();
+            res.preferredOrder = pkg.getPreferredOrder();
             res.match = match;
-            res.isDefault = info.hasDefault;
-            res.labelRes = info.labelRes;
-            res.nonLocalizedLabel = info.nonLocalizedLabel;
-            res.icon = info.icon;
+            res.isDefault = filter.hasDefault;
+            res.labelRes = filter.labelRes;
+            res.nonLocalizedLabel = filter.nonLocalizedLabel;
+            res.icon = filter.icon;
             res.system = res.providerInfo.applicationInfo.isSystemApp();
             return res;
         }
@@ -1547,26 +1678,37 @@
 
         @Override
         protected void dumpFilter(PrintWriter out, String prefix,
-                PackageParser.ProviderIntentInfo filter) {
+                ParsedProviderIntentInfo filter) {
+            ParsedProvider provider = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg != null && pkg.getProviders() != null) {
+                for (ParsedProvider parsedProvider : pkg.getProviders()) {
+                    if (Objects.equals(parsedProvider.className, filter.getClassName())) {
+                        provider = parsedProvider;
+                    }
+                }
+            }
+
             out.print(prefix);
-            out.print(Integer.toHexString(System.identityHashCode(filter.provider)));
+            out.print(Integer.toHexString(System.identityHashCode(provider)));
             out.print(' ');
-            filter.provider.printComponentShortName(out);
+            ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
             out.print(" filter ");
             out.println(Integer.toHexString(System.identityHashCode(filter)));
         }
 
         @Override
-        protected Object filterToLabel(PackageParser.ProviderIntentInfo filter) {
-            return filter.provider;
+        protected Object filterToLabel(ParsedProviderIntentInfo filter) {
+            return filter;
         }
 
         protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
-            final PackageParser.Provider provider = (PackageParser.Provider) label;
+            final ParsedProviderIntentInfo provider = (ParsedProviderIntentInfo) label;
             out.print(prefix);
             out.print(Integer.toHexString(System.identityHashCode(provider)));
             out.print(' ');
-            provider.printComponentShortName(out);
+            ComponentName.printShortString(out, provider.getPackageName(), provider.getClassName());
             if (count > 1) {
                 out.print(" (");
                 out.print(count);
@@ -1575,12 +1717,12 @@
             out.println();
         }
 
-        private final ArrayMap<ComponentName, PackageParser.Provider> mProviders = new ArrayMap<>();
+        private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
         private int mFlags;
     }
 
     private static final class ServiceIntentResolver
-            extends IntentResolver<PackageParser.ServiceIntentInfo, ResolveInfo> {
+            extends IntentResolver<ParsedServiceIntentInfo, ResolveInfo> {
         @Override
         public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
                 boolean defaultOnly, int userId) {
@@ -1598,22 +1740,22 @@
         }
 
         List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                int flags, List<PackageParser.Service> packageServices, int userId) {
+                int flags, List<ParsedService> packageServices, int userId) {
             if (!sUserManager.exists(userId)) return null;
             if (packageServices == null) {
-                return null;
+                return Collections.emptyList();
             }
             mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int servicesSize = packageServices.size();
-            ArrayList<PackageParser.ServiceIntentInfo[]> listCut = new ArrayList<>(servicesSize);
+            ArrayList<ParsedServiceIntentInfo[]> listCut = new ArrayList<>(servicesSize);
 
-            ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
+            List<ParsedServiceIntentInfo> intentFilters;
             for (int i = 0; i < servicesSize; ++i) {
                 intentFilters = packageServices.get(i).intents;
                 if (intentFilters != null && intentFilters.size() > 0) {
-                    PackageParser.ServiceIntentInfo[] array =
-                            new PackageParser.ServiceIntentInfo[intentFilters.size()];
+                    ParsedServiceIntentInfo[] array =
+                            new ParsedServiceIntentInfo[intentFilters.size()];
                     intentFilters.toArray(array);
                     listCut.add(array);
                 }
@@ -1621,40 +1763,40 @@
             return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
         }
 
-        void addService(PackageParser.Service s) {
+        void addService(ParsedService s) {
             mServices.put(s.getComponentName(), s);
             if (DEBUG_SHOW_INFO) {
                 Log.v(TAG, "  "
-                        + (s.info.nonLocalizedLabel != null
-                        ? s.info.nonLocalizedLabel : s.info.name) + ":");
-                Log.v(TAG, "    Class=" + s.info.name);
+                        + (s.nonLocalizedLabel != null
+                        ? s.nonLocalizedLabel : s.getName()) + ":");
+                Log.v(TAG, "    Class=" + s.getName());
             }
             final int intentsSize = s.intents.size();
             int j;
             for (j = 0; j < intentsSize; j++) {
-                PackageParser.ServiceIntentInfo intent = s.intents.get(j);
+                ParsedServiceIntentInfo intent = s.intents.get(j);
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
                 }
                 if (!intent.debugCheck()) {
-                    Log.w(TAG, "==> For Service " + s.info.name);
+                    Log.w(TAG, "==> For Service " + s.getName());
                 }
                 addFilter(intent);
             }
         }
 
-        void removeService(PackageParser.Service s) {
+        void removeService(ParsedService s) {
             mServices.remove(s.getComponentName());
             if (DEBUG_SHOW_INFO) {
-                Log.v(TAG, "  " + (s.info.nonLocalizedLabel != null
-                        ? s.info.nonLocalizedLabel : s.info.name) + ":");
-                Log.v(TAG, "    Class=" + s.info.name);
+                Log.v(TAG, "  " + (s.nonLocalizedLabel != null
+                        ? s.nonLocalizedLabel : s.getName()) + ":");
+                Log.v(TAG, "    Class=" + s.getName());
             }
             final int intentsSize = s.intents.size();
             int j;
             for (j = 0; j < intentsSize; j++) {
-                PackageParser.ServiceIntentInfo intent = s.intents.get(j);
+                ParsedServiceIntentInfo intent = s.intents.get(j);
                 if (DEBUG_SHOW_INFO) {
                     Log.v(TAG, "    IntentFilter:");
                     intent.dump(new LogPrinter(Log.VERBOSE, TAG), "      ");
@@ -1665,12 +1807,11 @@
 
         @Override
         protected boolean allowFilterResult(
-                PackageParser.ServiceIntentInfo filter, List<ResolveInfo> dest) {
-            ServiceInfo filterSi = filter.service.info;
+                ParsedServiceIntentInfo filter, List<ResolveInfo> dest) {
             for (int i = dest.size() - 1; i >= 0; --i) {
                 ServiceInfo destAi = dest.get(i).serviceInfo;
-                if (destAi.name == filterSi.name
-                        && destAi.packageName == filterSi.packageName) {
+                if (Objects.equals(destAi.name, filter.getClassName())
+                        && Objects.equals(destAi.packageName, filter.getPackageName())) {
                     return false;
                 }
             }
@@ -1678,48 +1819,69 @@
         }
 
         @Override
-        protected PackageParser.ServiceIntentInfo[] newArray(int size) {
-            return new PackageParser.ServiceIntentInfo[size];
+        protected ParsedServiceIntentInfo[] newArray(int size) {
+            return new ParsedServiceIntentInfo[size];
         }
 
         @Override
-        protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
+        protected boolean isFilterStopped(ParsedServiceIntentInfo filter, int userId) {
             if (!sUserManager.exists(userId)) return true;
-            PackageParser.Package p = filter.service.owner;
-            if (p != null) {
-                PackageSetting ps = (PackageSetting) p.mExtras;
-                if (ps != null) {
-                    // System apps are never considered stopped for purposes of
-                    // filtering, because there may be no way for the user to
-                    // actually re-launch them.
-                    return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
-                            && ps.getStopped(userId);
-                }
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg == null) {
+                return false;
             }
-            return false;
+
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    filter.getPackageName());
+            if (ps == null) {
+                return false;
+            }
+
+            // System apps are never considered stopped for purposes of
+            // filtering, because there may be no way for the user to
+            // actually re-launch them.
+            return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && ps.getStopped(userId);
         }
 
         @Override
         protected boolean isPackageForFilter(String packageName,
-                PackageParser.ServiceIntentInfo info) {
-            return packageName.equals(info.service.owner.packageName);
+                ParsedServiceIntentInfo info) {
+            return packageName.equals(info.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
+        protected ResolveInfo newResult(ParsedServiceIntentInfo filter,
                 int match, int userId) {
             if (!sUserManager.exists(userId)) return null;
-            final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo) filter;
-            if (!sPackageManagerInternal.isEnabledAndMatches(info.service.info, mFlags, userId)) {
+
+            ParsedService service = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg != null && pkg.getServices() != null) {
+                for (ParsedService parsedService : pkg.getServices()) {
+                    if (Objects.equals(parsedService.className, filter.getClassName())) {
+                        service = parsedService;
+                    }
+                }
+            }
+
+            if (service == null) {
                 return null;
             }
-            final PackageParser.Service service = info.service;
-            PackageSetting ps = (PackageSetting) service.owner.mExtras;
+
+            if (!sPackageManagerInternal.isEnabledAndMatches(service, mFlags, userId)) {
+                return null;
+            }
+
+            PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+                    filter.getPackageName());
             if (ps == null) {
                 return null;
             }
             final PackageUserState userState = ps.readUserState(userId);
-            ServiceInfo si = PackageParser.generateServiceInfo(service, mFlags,
+            ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
                     userState, userId);
             if (si == null) {
                 return null;
@@ -1729,7 +1891,7 @@
             final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp
-                    && !(info.isVisibleToInstantApp() || userState.instantApp)) {
+                    && !(filter.isVisibleToInstantApp() || userState.instantApp)) {
                 return null;
             }
             // throw out ephemeral filters if we're not explicitly requesting them
@@ -1746,13 +1908,13 @@
             if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
-            res.priority = info.getPriority();
-            res.preferredOrder = service.owner.mPreferredOrder;
+            res.priority = filter.getPriority();
+            res.preferredOrder = pkg.getPreferredOrder();
             res.match = match;
-            res.isDefault = info.hasDefault;
-            res.labelRes = info.labelRes;
-            res.nonLocalizedLabel = info.nonLocalizedLabel;
-            res.icon = info.icon;
+            res.isDefault = filter.hasDefault;
+            res.labelRes = filter.labelRes;
+            res.nonLocalizedLabel = filter.nonLocalizedLabel;
+            res.icon = filter.icon;
             res.system = res.serviceInfo.applicationInfo.isSystemApp();
             return res;
         }
@@ -1764,31 +1926,42 @@
 
         @Override
         protected void dumpFilter(PrintWriter out, String prefix,
-                PackageParser.ServiceIntentInfo filter) {
+                ParsedServiceIntentInfo filter) {
+            ParsedService service = null;
+
+            AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
+            if (pkg != null && pkg.getServices() != null) {
+                for (ParsedService parsedService : pkg.getServices()) {
+                    if (Objects.equals(parsedService.className, filter.getClassName())) {
+                        service = parsedService;
+                    }
+                }
+            }
+
             out.print(prefix);
-            out.print(Integer.toHexString(System.identityHashCode(filter.service)));
+            out.print(Integer.toHexString(System.identityHashCode(service)));
             out.print(' ');
-            filter.service.printComponentShortName(out);
+            ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
             out.print(" filter ");
             out.print(Integer.toHexString(System.identityHashCode(filter)));
-            if (filter.service.info.permission != null) {
-                out.print(" permission "); out.println(filter.service.info.permission);
+            if (service != null && service.getPermission() != null) {
+                out.print(" permission "); out.println(service.getPermission());
             } else {
                 out.println();
             }
         }
 
         @Override
-        protected Object filterToLabel(PackageParser.ServiceIntentInfo filter) {
-            return filter.service;
+        protected Object filterToLabel(ParsedServiceIntentInfo filter) {
+            return filter;
         }
 
         protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
-            final PackageParser.Service service = (PackageParser.Service) label;
+            final ParsedServiceIntentInfo service = (ParsedServiceIntentInfo) label;
             out.print(prefix);
             out.print(Integer.toHexString(System.identityHashCode(service)));
             out.print(' ');
-            service.printComponentShortName(out);
+            ComponentName.printShortString(out, service.getPackageName(), service.getClassName());
             if (count > 1) {
                 out.print(" ("); out.print(count); out.print(" filters)");
             }
@@ -1796,7 +1969,7 @@
         }
 
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, PackageParser.Service> mServices = new ArrayMap<>();
+        private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
         private int mFlags;
     }
 
@@ -1885,7 +2058,7 @@
 
     /** Generic to create an {@link Iterator} for a data type */
     static class IterGenerator<E> {
-        public Iterator<E> generate(ActivityIntentInfo info) {
+        public Iterator<E> generate(ParsedActivityIntentInfo info) {
             return null;
         }
     }
@@ -1893,7 +2066,7 @@
     /** Create an {@link Iterator} for intent actions */
     static class ActionIterGenerator extends IterGenerator<String> {
         @Override
-        public Iterator<String> generate(ActivityIntentInfo info) {
+        public Iterator<String> generate(ParsedActivityIntentInfo info) {
             return info.actionsIterator();
         }
     }
@@ -1901,7 +2074,7 @@
     /** Create an {@link Iterator} for intent categories */
     static class CategoriesIterGenerator extends IterGenerator<String> {
         @Override
-        public Iterator<String> generate(ActivityIntentInfo info) {
+        public Iterator<String> generate(ParsedActivityIntentInfo info) {
             return info.categoriesIterator();
         }
     }
@@ -1909,7 +2082,7 @@
     /** Create an {@link Iterator} for intent schemes */
     static class SchemesIterGenerator extends IterGenerator<String> {
         @Override
-        public Iterator<String> generate(ActivityIntentInfo info) {
+        public Iterator<String> generate(ParsedActivityIntentInfo info) {
             return info.schemesIterator();
         }
     }
@@ -1917,9 +2090,39 @@
     /** Create an {@link Iterator} for intent authorities */
     static class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
         @Override
-        public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) {
+        public Iterator<IntentFilter.AuthorityEntry> generate(ParsedActivityIntentInfo info) {
             return info.authoritiesIterator();
         }
     }
 
+    // TODO(b/135203078): Document or remove this if possible.
+    class EffectiveProvider extends ParsedProvider {
+
+        private String mEffectiveAuthority;
+        private boolean mEffectiveSyncable;
+
+        public EffectiveProvider(ParsedProvider parsedProvider) {
+            this.setFrom(parsedProvider);
+            this.mEffectiveAuthority = parsedProvider.getAuthority();
+            this.mEffectiveSyncable = parsedProvider.isSyncable();
+        }
+
+        public void setEffectiveAuthority(String authority) {
+            this.mEffectiveAuthority = authority;
+        }
+
+        public void setEffectiveSyncable(boolean syncable) {
+            this.mEffectiveSyncable = syncable;
+        }
+
+        @Override
+        public String getAuthority() {
+            return mEffectiveAuthority;
+        }
+
+        @Override
+        public boolean isSyncable() {
+            return mEffectiveSyncable;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 9e04c4b..f9113fa 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -20,9 +20,11 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -137,7 +139,7 @@
     public byte[] getInstantAppCookieLPw(@NonNull String packageName,
             @UserIdInt int userId) {
         // Only installed packages can get their own cookie
-        PackageParser.Package pkg = mService.mPackages.get(packageName);
+        AndroidPackage pkg = mService.mPackages.get(packageName);
         if (pkg == null) {
             return null;
         }
@@ -171,7 +173,7 @@
         }
 
         // Only an installed package can set its own cookie
-        PackageParser.Package pkg = mService.mPackages.get(packageName);
+        AndroidPackage pkg = mService.mPackages.get(packageName);
         if (pkg == null) {
             return false;
         }
@@ -264,15 +266,15 @@
     }
 
     @GuardedBy("mService.mLock")
-    public void onPackageInstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
+    public void onPackageInstalledLPw(@NonNull AndroidPackage pkg, @NonNull int[] userIds) {
+        PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
         if (ps == null) {
             return;
         }
 
         for (int userId : userIds) {
             // Ignore not installed apps
-            if (mService.mPackages.get(pkg.packageName) == null || !ps.getInstalled(userId)) {
+            if (mService.mPackages.get(pkg.getPackageName()) == null || !ps.getInstalled(userId)) {
                 continue;
             }
 
@@ -286,16 +288,16 @@
 
             // Remove the in-memory state
             removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
-                            state.mInstantAppInfo.getPackageName().equals(pkg.packageName),
+                            state.mInstantAppInfo.getPackageName().equals(pkg.getPackageName()),
                     userId);
 
             // Remove the on-disk state except the cookie
-            File instantAppDir = getInstantApplicationDir(pkg.packageName, userId);
+            File instantAppDir = getInstantApplicationDir(pkg.getPackageName(), userId);
             new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
             new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
 
             // If app signature changed - wipe the cookie
-            File currentCookieFile = peekInstantCookieFile(pkg.packageName, userId);
+            File currentCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
             if (currentCookieFile == null) {
                 continue;
             }
@@ -310,7 +312,7 @@
             // We prefer the modern computation procedure where all certs are taken
             // into account but also allow the value from the old computation to avoid
             // data loss.
-            if (pkg.mSigningDetails.checkCapability(currentCookieSha256,
+            if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
                     PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
                 return;
             }
@@ -318,7 +320,7 @@
             // For backwards compatibility we accept match based on any signature, since we may have
             // recorded only the first for multiply-signed packages
             final String[] signaturesSha256Digests =
-                    PackageUtils.computeSignaturesSha256Digests(pkg.mSigningDetails.signatures);
+                    PackageUtils.computeSignaturesSha256Digests(pkg.getSigningDetails().signatures);
             for (String s : signaturesSha256Digests) {
                 if (s.equals(currentCookieSha256)) {
                     return;
@@ -326,7 +328,7 @@
             }
 
             // Sorry, you are out of luck - different signatures - nuke data
-            Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
+            Slog.i(LOG_TAG, "Signature for package " + pkg.getPackageName()
                     + " changed - dropping cookie");
                 // Make sure a pending write for the old signed app is cancelled
             mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
@@ -335,15 +337,15 @@
     }
 
     @GuardedBy("mService.mLock")
-    public void onPackageUninstalledLPw(@NonNull PackageParser.Package pkg,
+    public void onPackageUninstalledLPw(@NonNull AndroidPackage pkg,
             @NonNull int[] userIds) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
         if (ps == null) {
             return;
         }
 
         for (int userId : userIds) {
-            if (mService.mPackages.get(pkg.packageName) != null && ps.getInstalled(userId)) {
+            if (mService.mPackages.get(pkg.getPackageName()) != null && ps.getInstalled(userId)) {
                 continue;
             }
 
@@ -353,7 +355,7 @@
                 removeInstantAppLPw(userId, ps.appId);
             } else {
                 // Deleting an app prunes all instant state such as cookie
-                deleteDir(getInstantApplicationDir(pkg.packageName, userId));
+                deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
                 mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
                 removeAppLPw(userId, ps.appId);
             }
@@ -487,7 +489,7 @@
     }
 
     @GuardedBy("mService.mLock")
-    private void addUninstalledInstantAppLPw(@NonNull PackageParser.Package pkg,
+    private void addUninstalledInstantAppLPw(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
         InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
                 pkg, userId, false);
@@ -511,14 +513,15 @@
         writeInstantApplicationIconLPw(pkg, userId);
     }
 
-    private void writeInstantApplicationIconLPw(@NonNull PackageParser.Package pkg,
+    private void writeInstantApplicationIconLPw(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
-        File appDir = getInstantApplicationDir(pkg.packageName, userId);
+        File appDir = getInstantApplicationDir(pkg.getPackageName(), userId);
         if (!appDir.exists()) {
             return;
         }
 
-        Drawable icon = pkg.applicationInfo.loadIcon(mService.mContext.getPackageManager());
+        // TODO(b/135203078): Remove toAppInfo call? Requires significant additions/changes to PM
+        Drawable icon = pkg.toAppInfo().loadIcon(mService.mContext.getPackageManager());
 
         final Bitmap bitmap;
         if (icon instanceof BitmapDrawable) {
@@ -531,7 +534,7 @@
             icon.draw(canvas);
         }
 
-        File iconFile = new File(getInstantApplicationDir(pkg.packageName, userId),
+        File iconFile = new File(getInstantApplicationDir(pkg.getPackageName(), userId),
                 INSTANT_APP_ICON_FILE);
 
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
@@ -690,14 +693,16 @@
 
             final int packageCount = mService.mPackages.size();
             for (int i = 0; i < packageCount; i++) {
-                final PackageParser.Package pkg = mService.mPackages.valueAt(i);
+                final AndroidPackage pkg = mService.mPackages.valueAt(i);
                 if (now - pkg.getLatestPackageUseTimeInMills() < maxInstalledCacheDuration) {
                     continue;
                 }
-                if (!(pkg.mExtras instanceof PackageSetting)) {
+
+                final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+                if (ps == null) {
                     continue;
                 }
-                final PackageSetting  ps = (PackageSetting) pkg.mExtras;
+
                 boolean installedOnlyAsInstantApp = false;
                 for (int userId : allUsers) {
                     if (ps.getInstalled(userId)) {
@@ -713,14 +718,14 @@
                     if (packagesToDelete == null) {
                         packagesToDelete = new ArrayList<>();
                     }
-                    packagesToDelete.add(pkg.packageName);
+                    packagesToDelete.add(pkg.getPackageName());
                 }
             }
 
             if (packagesToDelete != null) {
                 packagesToDelete.sort((String lhs, String rhs) -> {
-                    final PackageParser.Package lhsPkg = mService.mPackages.get(lhs);
-                    final PackageParser.Package rhsPkg = mService.mPackages.get(rhs);
+                    final AndroidPackage lhsPkg = mService.mPackages.get(lhs);
+                    final AndroidPackage rhsPkg = mService.mPackages.get(rhs);
                     if (lhsPkg == null && rhsPkg == null) {
                         return 0;
                     } else if (lhsPkg == null) {
@@ -735,18 +740,23 @@
                                 rhsPkg.getLatestPackageUseTimeInMills()) {
                             return -1;
                         } else {
-                            if (lhsPkg.mExtras instanceof PackageSetting
-                                    && rhsPkg.mExtras instanceof PackageSetting) {
-                                final PackageSetting lhsPs = (PackageSetting) lhsPkg.mExtras;
-                                final PackageSetting rhsPs = (PackageSetting) rhsPkg.mExtras;
-                                if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
-                                    return 1;
-                                } else {
-                                    return -1;
-                                }
-                            } else {
+                            final PackageSetting lhsPs = mService.getPackageSetting(
+                                    lhsPkg.getPackageName());
+                            if (lhsPs == null) {
                                 return 0;
                             }
+
+                            final PackageSetting rhsPs = mService.getPackageSetting(
+                                    rhsPkg.getPackageName());
+                            if (rhsPs == null) {
+                                return 0;
+                            }
+
+                            if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
+                                return 1;
+                            } else {
+                                return -1;
+                            }
                         }
                     }
                 });
@@ -818,8 +828,8 @@
 
         final int packageCount = mService.mPackages.size();
         for (int i = 0; i < packageCount; i++) {
-            final PackageParser.Package pkg = mService.mPackages.valueAt(i);
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            final AndroidPackage pkg = mService.mPackages.valueAt(i);
+            final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
             if (ps == null || !ps.getInstantApp(userId)) {
                 continue;
             }
@@ -839,9 +849,9 @@
 
     private @NonNull
     InstantAppInfo createInstantAppInfoForPackage(
-            @NonNull PackageParser.Package pkg, @UserIdInt int userId,
+            @NonNull AndroidPackage pkg, @UserIdInt int userId,
             boolean addApplicationInfo) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
         if (ps == null) {
             return null;
         }
@@ -849,19 +859,20 @@
             return null;
         }
 
-        String[] requestedPermissions = new String[pkg.requestedPermissions.size()];
-        pkg.requestedPermissions.toArray(requestedPermissions);
+        String[] requestedPermissions = new String[pkg.getRequestedPermissions().size()];
+        pkg.getRequestedPermissions().toArray(requestedPermissions);
 
         Set<String> permissions = ps.getPermissionsState().getPermissions(userId);
         String[] grantedPermissions = new String[permissions.size()];
         permissions.toArray(grantedPermissions);
 
+        ApplicationInfo appInfo = pkg.toAppInfo();
         if (addApplicationInfo) {
-            return new InstantAppInfo(pkg.applicationInfo,
+            return new InstantAppInfo(appInfo,
                     requestedPermissions, grantedPermissions);
         } else {
-            return new InstantAppInfo(pkg.applicationInfo.packageName,
-                    pkg.applicationInfo.loadLabel(mService.mContext.getPackageManager()),
+            return new InstantAppInfo(appInfo.packageName,
+                    appInfo.loadLabel(mService.mContext.getPackageManager()),
                     requestedPermissions, grantedPermissions);
         }
     }
@@ -887,10 +898,10 @@
         return uninstalledApps;
     }
 
-    private void propagateInstantAppPermissionsIfNeeded(@NonNull PackageParser.Package pkg,
+    private void propagateInstantAppPermissionsIfNeeded(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
         InstantAppInfo appInfo = peekOrParseUninstalledInstantAppInfo(
-                pkg.packageName, userId);
+                pkg.getPackageName(), userId);
         if (appInfo == null) {
             return;
         }
@@ -902,8 +913,10 @@
             for (String grantedPermission : appInfo.getGrantedPermissions()) {
                 final boolean propagatePermission =
                         mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
-                if (propagatePermission && pkg.requestedPermissions.contains(grantedPermission)) {
-                    mService.grantRuntimePermission(pkg.packageName, grantedPermission, userId);
+                if (propagatePermission && pkg.getRequestedPermissions().contains(
+                        grantedPermission)) {
+                    mService.grantRuntimePermission(pkg.getPackageName(), grantedPermission,
+                            userId);
                 }
             }
         } finally {
@@ -1188,18 +1201,19 @@
             super(looper);
         }
 
-        public void schedulePersistLPw(@UserIdInt int userId, @NonNull PackageParser.Package pkg,
+        public void schedulePersistLPw(@UserIdInt int userId, @NonNull AndroidPackage pkg,
                 @NonNull byte[] cookie) {
             // Before we used only the first signature to compute the SHA 256 but some
             // apps could be singed by multiple certs and the cert order is undefined.
             // We prefer the modern computation procedure where all certs are taken
             // into account and delete the file derived via the legacy hash computation.
-            File newCookieFile = computeInstantCookieFile(pkg.packageName,
-                    PackageUtils.computeSignaturesSha256Digest(pkg.mSigningDetails.signatures), userId);
-            if (!pkg.mSigningDetails.hasSignatures()) {
+            File newCookieFile = computeInstantCookieFile(pkg.getPackageName(),
+                    PackageUtils.computeSignaturesSha256Digest(pkg.getSigningDetails().signatures),
+                    userId);
+            if (!pkg.getSigningDetails().hasSignatures()) {
                 Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
             }
-            File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId);
+            File oldCookieFile = peekInstantCookieFile(pkg.getPackageName(), userId);
             if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) {
                 oldCookieFile.delete();
             }
@@ -1209,12 +1223,12 @@
                     PERSIST_COOKIE_DELAY_MILLIS);
         }
 
-        public @Nullable byte[] getPendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
+        public @Nullable byte[] getPendingPersistCookieLPr(@NonNull AndroidPackage pkg,
                 @UserIdInt int userId) {
             ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
             if (pendingWorkForUser != null) {
-                SomeArgs state = pendingWorkForUser.get(pkg.packageName);
+                SomeArgs state = pendingWorkForUser.get(pkg.getPackageName());
                 if (state != null) {
                     return (byte[]) state.arg1;
                 }
@@ -1222,7 +1236,7 @@
             return null;
         }
 
-        public void cancelPendingPersistLPw(@NonNull PackageParser.Package pkg,
+        public void cancelPendingPersistLPw(@NonNull AndroidPackage pkg,
                 @UserIdInt int userId) {
             removeMessages(userId, pkg);
             SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
@@ -1232,7 +1246,7 @@
         }
 
         private void addPendingPersistCookieLPw(@UserIdInt int userId,
-                @NonNull PackageParser.Package pkg, @NonNull byte[] cookie,
+                @NonNull AndroidPackage pkg, @NonNull byte[] cookie,
                 @NonNull File cookieFile) {
             ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
@@ -1243,16 +1257,16 @@
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = cookie;
             args.arg2 = cookieFile;
-            pendingWorkForUser.put(pkg.packageName, args);
+            pendingWorkForUser.put(pkg.getPackageName(), args);
         }
 
-        private SomeArgs removePendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
+        private SomeArgs removePendingPersistCookieLPr(@NonNull AndroidPackage pkg,
                 @UserIdInt int userId) {
             ArrayMap<String, SomeArgs> pendingWorkForUser =
                     mPendingPersistCookies.get(userId);
             SomeArgs state = null;
             if (pendingWorkForUser != null) {
-                state = pendingWorkForUser.remove(pkg.packageName);
+                state = pendingWorkForUser.remove(pkg.getPackageName());
                 if (pendingWorkForUser.isEmpty()) {
                     mPendingPersistCookies.remove(userId);
                 }
@@ -1263,7 +1277,7 @@
         @Override
         public void handleMessage(Message message) {
             int userId = message.what;
-            PackageParser.Package pkg = (PackageParser.Package) message.obj;
+            AndroidPackage pkg = (AndroidPackage) message.obj;
             SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
             if (state == null) {
                 return;
@@ -1271,7 +1285,7 @@
             byte[] cookie = (byte[]) state.arg1;
             File cookieFile = (File) state.arg2;
             state.recycle();
-            persistInstantApplicationCookie(cookie, pkg.packageName, cookieFile, userId);
+            persistInstantApplicationCookie(cookie, pkg.getPackageName(), cookieFile, userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java
index ec48713..0a065eb 100644
--- a/services/core/java/com/android/server/pm/InstructionSets.java
+++ b/services/core/java/com/android/server/pm/InstructionSets.java
@@ -16,7 +16,7 @@
 
 package com.android.server.pm;
 
-import android.content.pm.ApplicationInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.text.TextUtils;
@@ -35,30 +35,16 @@
 public class InstructionSets {
     private static final String PREFERRED_INSTRUCTION_SET =
             VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-    public static String[] getAppDexInstructionSets(ApplicationInfo info) {
-        if (info.primaryCpuAbi != null) {
-            if (info.secondaryCpuAbi != null) {
+
+    public static String[] getAppDexInstructionSets(String primaryCpuAbi, String secondaryCpuAbi) {
+        if (primaryCpuAbi != null) {
+            if (secondaryCpuAbi != null) {
                 return new String[] {
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi),
-                        VMRuntime.getInstructionSet(info.secondaryCpuAbi) };
+                        VMRuntime.getInstructionSet(primaryCpuAbi),
+                        VMRuntime.getInstructionSet(secondaryCpuAbi) };
             } else {
                 return new String[] {
-                        VMRuntime.getInstructionSet(info.primaryCpuAbi) };
-            }
-        }
-
-        return new String[] { getPreferredInstructionSet() };
-    }
-
-    public static String[] getAppDexInstructionSets(PackageSetting ps) {
-        if (ps.primaryCpuAbiString != null) {
-            if (ps.secondaryCpuAbiString != null) {
-                return new String[] {
-                        VMRuntime.getInstructionSet(ps.primaryCpuAbiString),
-                        VMRuntime.getInstructionSet(ps.secondaryCpuAbiString) };
-            } else {
-                return new String[] {
-                        VMRuntime.getInstructionSet(ps.primaryCpuAbiString) };
+                        VMRuntime.getInstructionSet(primaryCpuAbi) };
             }
         }
 
@@ -124,4 +110,12 @@
         return VMRuntime.getInstructionSet(abis.primary);
     }
 
+    public static String getPrimaryInstructionSet(AndroidPackage pkg) {
+        if (pkg.getPrimaryCpuAbi() == null) {
+            return getPreferredInstructionSet();
+        }
+
+        return VMRuntime.getInstructionSet(pkg.getPrimaryCpuAbi());
+    }
+
 }
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
index a4e9d10..c97d85d 100644
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -17,7 +17,7 @@
 package com.android.server.pm;
 
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ComponentParseUtils;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -35,7 +35,7 @@
 
     private int mState;
 
-    private ArrayList<PackageParser.ActivityIntentInfo> mFilters = new ArrayList<>();
+    private ArrayList<ComponentParseUtils.ParsedActivityIntentInfo> mFilters = new ArrayList<>();
     private ArraySet<String> mHosts = new ArraySet<>();
     private int mUserId;
 
@@ -66,7 +66,7 @@
         setState(STATE_VERIFICATION_PENDING);
     }
 
-    public ArrayList<PackageParser.ActivityIntentInfo> getFilters() {
+    public ArrayList<ComponentParseUtils.ParsedActivityIntentInfo> getFilters() {
         return mFilters;
     }
 
@@ -123,7 +123,7 @@
         return false;
     }
 
-    public void addFilter(PackageParser.ActivityIntentInfo filter) {
+    public void addFilter(ComponentParseUtils.ParsedActivityIntentInfo filter) {
         mFilters.add(filter);
         mHosts.addAll(filter.getHostsList());
     }
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 93d3b77..70c0f8d 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -20,23 +20,26 @@
 
 import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
 
-import com.android.internal.util.Preconditions;
 import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Base64;
-import android.util.Slog;
 import android.util.LongSparseArray;
+import android.util.Slog;
 
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.security.PublicKey;
-import java.util.Set;
+import com.android.internal.util.Preconditions;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.PublicKey;
+import java.util.Map;
+import java.util.Set;
+
 /*
  * Manages system-wide KeySet state.
  */
@@ -182,33 +185,31 @@
      *
      * Returns true if the package can safely be added to the keyset metadata.
      */
-    public void assertScannedPackageValid(PackageParser.Package pkg)
+    public void assertScannedPackageValid(AndroidPackage pkg)
             throws PackageManagerException {
-        if (pkg == null || pkg.packageName == null) {
+        if (pkg == null || pkg.getPackageName() == null) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Passed invalid package to keyset validation.");
         }
-        ArraySet<PublicKey> signingKeys = pkg.mSigningDetails.publicKeys;
+        ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().publicKeys;
         if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Package has invalid signing-key-set.");
         }
-        ArrayMap<String, ArraySet<PublicKey>> definedMapping = pkg.mKeySetMapping;
+        Map<String, ArraySet<PublicKey>> definedMapping = pkg.getKeySetMapping();
         if (definedMapping != null) {
             if (definedMapping.containsKey(null) || definedMapping.containsValue(null)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Package has null defined key set.");
             }
-            int defMapSize = definedMapping.size();
-            for (int i = 0; i < defMapSize; i++) {
-                if (!(definedMapping.valueAt(i).size() > 0)
-                        || definedMapping.valueAt(i).contains(null)) {
+            for (ArraySet<PublicKey> value : definedMapping.values()) {
+                if (!(value.size() > 0) || value.contains(null)) {
                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                             "Package has null/no public keys for defined key-sets.");
                 }
             }
         }
-        ArraySet<String> upgradeAliases = pkg.mUpgradeKeySets;
+        Set<String> upgradeAliases = pkg.getUpgradeKeySets();
         if (upgradeAliases != null) {
             if (definedMapping == null || !(definedMapping.keySet().containsAll(upgradeAliases))) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -217,17 +218,17 @@
         }
     }
 
-    public void addScannedPackageLPw(PackageParser.Package pkg) {
+    public void addScannedPackageLPw(AndroidPackage pkg) {
         Preconditions.checkNotNull(pkg, "Attempted to add null pkg to ksms.");
-        Preconditions.checkNotNull(pkg.packageName, "Attempted to add null pkg to ksms.");
-        PackageSetting ps = mPackages.get(pkg.packageName);
-        Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName
+        Preconditions.checkNotNull(pkg.getPackageName(), "Attempted to add null pkg to ksms.");
+        PackageSetting ps = mPackages.get(pkg.getPackageName());
+        Preconditions.checkNotNull(ps, "pkg: " + pkg.getPackageName()
                     + "does not have a corresponding entry in mPackages.");
-        addSigningKeySetToPackageLPw(ps, pkg.mSigningDetails.publicKeys);
-        if (pkg.mKeySetMapping != null) {
-            addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping);
-            if (pkg.mUpgradeKeySets != null) {
-                addUpgradeKeySetsToPackageLPw(ps, pkg.mUpgradeKeySets);
+        addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
+        if (pkg.getKeySetMapping() != null) {
+            addDefinedKeySetsToPackageLPw(ps, pkg.getKeySetMapping());
+            if (pkg.getUpgradeKeySets() != null) {
+                addUpgradeKeySetsToPackageLPw(ps, pkg.getUpgradeKeySets());
             }
         }
     }
@@ -280,15 +281,14 @@
      * Remove any KeySets the package no longer defines.
      */
     void addDefinedKeySetsToPackageLPw(PackageSetting pkg,
-            ArrayMap<String, ArraySet<PublicKey>> definedMapping) {
+            Map<String, ArraySet<PublicKey>> definedMapping) {
         ArrayMap<String, Long> prevDefinedKeySets = pkg.keySetData.getAliases();
 
         /* add all of the newly defined KeySets */
-        ArrayMap<String, Long> newKeySetAliases = new ArrayMap<String, Long>();
-        final int defMapSize = definedMapping.size();
-        for (int i = 0; i < defMapSize; i++) {
-            String alias = definedMapping.keyAt(i);
-            ArraySet<PublicKey> pubKeys = definedMapping.valueAt(i);
+        Map<String, Long> newKeySetAliases = new ArrayMap<>();
+        for (Map.Entry<String, ArraySet<PublicKey>> entry : definedMapping.entrySet()) {
+            String alias = entry.getKey();
+            ArraySet<PublicKey> pubKeys = entry.getValue();
             if (alias != null && pubKeys != null && pubKeys.size() > 0) {
                 KeySetHandle ks = addKeySetLPw(pubKeys);
                 newKeySetAliases.put(alias, ks.getId());
@@ -313,12 +313,10 @@
      * after all of the defined KeySets have been added.
      */
     void addUpgradeKeySetsToPackageLPw(PackageSetting pkg,
-            ArraySet<String> upgradeAliases) {
-        final int uaSize = upgradeAliases.size();
-        for (int i = 0; i < uaSize; i++) {
-            pkg.keySetData.addUpgradeKeySet(upgradeAliases.valueAt(i));
+            Set<String> upgradeAliases) {
+        for (String upgradeAlias : upgradeAliases) {
+            pkg.keySetData.addUpgradeKeySet(upgradeAlias);
         }
-        return;
     }
 
     /**
@@ -364,14 +362,14 @@
         return true;
     }
 
-    public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS,
-            PackageParser.Package newPkg) {
+    public boolean checkUpgradeKeySetLocked(PackageSettingBase oldPS, AndroidPackage pkg) {
         // Upgrade keysets are being used.  Determine if new package has a superset of the
         // required keys.
         long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
         for (int i = 0; i < upgradeKeySets.length; i++) {
             Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
-            if (upgradeSet != null && newPkg.mSigningDetails.publicKeys.containsAll(upgradeSet)) {
+            if (upgradeSet != null
+                    && pkg.getSigningDetails().publicKeys.containsAll(upgradeSet)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3464cab..c844f1b 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -40,13 +40,13 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
@@ -445,7 +445,7 @@
             }
             final PackageManagerInternal pmInt =
                     LocalServices.getService(PackageManagerInternal.class);
-            final PackageParser.Package pkg = pmInt.getPackage(appInfo.packageName);
+            final AndroidPackage pkg = pmInt.getPackage(appInfo.packageName);
             if (pkg == null) {
                 // Should not happen, but we shouldn't be failing if it does
                 return false;
@@ -456,8 +456,8 @@
                     appInfo.packageName);
         }
 
-        private boolean requestsPermissions(@NonNull PackageParser.Package pkg) {
-            return !ArrayUtils.isEmpty(pkg.requestedPermissions);
+        private boolean requestsPermissions(@NonNull AndroidPackage pkg) {
+            return !ArrayUtils.isEmpty(pkg.getRequestedPermissions());
         }
 
         private boolean hasDefaultEnableLauncherActivity(@NonNull String packageName) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index d49ecdd..ae7a4a7 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -22,7 +22,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.IOtaDexopt;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -118,8 +118,8 @@
         if (mDexoptCommands != null) {
             throw new IllegalStateException("already called prepare()");
         }
-        final List<PackageParser.Package> important;
-        final List<PackageParser.Package> others;
+        final List<AndroidPackage> important;
+        final List<AndroidPackage> others;
         synchronized (mPackageManagerService.mLock) {
             // Important: the packages we need to run with ab-ota compiler-reason.
             important = PackageManagerServiceUtils.getPackagesForDexopt(
@@ -133,12 +133,12 @@
             mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
         }
 
-        for (PackageParser.Package p : important) {
+        for (AndroidPackage p : important) {
             mDexoptCommands.addAll(generatePackageDexopts(p, PackageManagerService.REASON_AB_OTA));
         }
-        for (PackageParser.Package p : others) {
+        for (AndroidPackage p : others) {
             // We assume here that there are no core apps left.
-            if (p.coreApp) {
+            if (p.isCoreApp()) {
                 throw new IllegalStateException("Found a core app that's not important");
             }
             mDexoptCommands.addAll(
@@ -150,8 +150,8 @@
         if (spaceAvailable < BULK_DELETE_THRESHOLD) {
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
                     + PackageManagerServiceUtils.packagesToString(others));
-            for (PackageParser.Package pkg : others) {
-                mPackageManagerService.deleteOatArtifactsOfPackage(pkg.packageName);
+            for (AndroidPackage pkg : others) {
+                mPackageManagerService.deleteOatArtifactsOfPackage(pkg.getPackageName());
             }
         }
         long spaceAvailableNow = getAvailableSpace();
@@ -161,15 +161,15 @@
         if (DEBUG_DEXOPT) {
             try {
                 // Output some data about the packages.
-                PackageParser.Package lastUsed = Collections.max(important,
+                AndroidPackage lastUsed = Collections.max(important,
                         (pkg1, pkg2) -> Long.compare(
                                 pkg1.getLatestForegroundPackageUseTimeInMills(),
                                 pkg2.getLatestForegroundPackageUseTimeInMills()));
                 Log.d(TAG, "A/B OTA: lastUsed time = "
                         + lastUsed.getLatestForegroundPackageUseTimeInMills());
                 Log.d(TAG, "A/B OTA: deprioritized packages:");
-                for (PackageParser.Package pkg : others) {
-                    Log.d(TAG, "  " + pkg.packageName + " - "
+                for (AndroidPackage pkg : others) {
+                    Log.d(TAG, "  " + pkg.getPackageName() + " - "
                             + pkg.getLatestForegroundPackageUseTimeInMills());
                 }
             } catch (Exception ignored) {
@@ -262,7 +262,7 @@
     /**
      * Generate all dexopt commands for the given package.
      */
-    private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
+    private synchronized List<String> generatePackageDexopts(AndroidPackage pkg,
             int compilationReason) {
         // Intercept and collect dexopt requests
         final List<String> commands = new ArrayList<String>();
@@ -336,8 +336,9 @@
         optimizer.performDexOpt(pkg,
                 null /* ISAs */,
                 null /* CompilerStats.PackageStats */,
-                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
-                new DexoptOptions(pkg.packageName, compilationReason,
+                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
+                        pkg.getPackageName()),
+                new DexoptOptions(pkg.getPackageName(), compilationReason,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE));
 
         return commands;
@@ -359,10 +360,10 @@
         }
 
         // Look into all packages.
-        Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages();
+        Collection<AndroidPackage> pkgs = mPackageManagerService.getPackages();
         int packagePaths = 0;
         int pathsSuccessful = 0;
-        for (PackageParser.Package pkg : pkgs) {
+        for (AndroidPackage pkg : pkgs) {
             if (pkg == null) {
                 continue;
             }
@@ -371,27 +372,28 @@
             if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
                 continue;
             }
-            if (pkg.codePath == null) {
+            if (pkg.getCodePath() == null) {
                 Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
                 continue;
             }
 
             // If the path is in /system, /vendor, /product or /system_ext, ignore. It will
             // have been ota-dexopted into /data/ota and moved into the dalvik-cache already.
-            if (pkg.codePath.startsWith("/system")
-                    || pkg.codePath.startsWith("/vendor")
-                    || pkg.codePath.startsWith("/product")
-                    || pkg.codePath.startsWith("/system_ext")) {
+            if (pkg.getCodePath().startsWith("/system")
+                    || pkg.getCodePath().startsWith("/vendor")
+                    || pkg.getCodePath().startsWith("/product")
+                    || pkg.getCodePath().startsWith("/system_ext")) {
                 continue;
             }
 
-            final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+            final String[] instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
+                    pkg.getSecondaryCpuAbi());
             final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
             final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
             for (String dexCodeInstructionSet : dexCodeInstructionSets) {
                 for (String path : paths) {
-                    String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)).
-                            getAbsolutePath();
+                    String oatDir = PackageDexOptimizer.getOatDir(
+                            new File(pkg.getCodePath())).getAbsolutePath();
 
                     // TODO: Check first whether there is an artifact, to save the roundtrip time.
 
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index c21d0cf..d7c161c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -17,7 +17,8 @@
 package com.android.server.pm;
 
 import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
 import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -25,21 +26,21 @@
 import java.io.File;
 import java.util.Set;
 
+// TODO: Move to .parsing sub-package
 @VisibleForTesting
 public interface PackageAbiHelper {
     /**
      * Derive and get the location of native libraries for the given package,
      * which varies depending on where and how the package was installed.
      */
-    NativeLibraryPaths getNativeLibraryPaths(
-            PackageParser.Package pkg, File appLib32InstallDir);
+    NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, File appLib32InstallDir);
 
     /**
      * Calculate the abis for a bundled app. These can uniquely be determined from the contents of
      * the system partition, i.e whether it contains 64 or 32 bit shared libraries etc. We do not
      * validate any of this information, and instead assume that the system was built sensibly.
      */
-    Abis getBundledAppAbis(PackageParser.Package pkg);
+    Abis getBundledAppAbis(AndroidPackage pkg);
 
     /**
      * Derive the ABI of a non-system package located at {@code pkg}. This information
@@ -48,7 +49,7 @@
      * If {@code extractLibs} is true, native libraries are extracted from the app if required.
      */
     Pair<Abis, NativeLibraryPaths> derivePackageAbi(
-            PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs)
+            AndroidPackage pkg, String cpuAbiOverride, boolean extractLibs)
             throws PackageManagerException;
 
     /**
@@ -69,11 +70,11 @@
      */
     @Nullable
     String getAdjustedAbiForSharedUser(
-            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage);
+            Set<PackageSetting> packagesForUser, AndroidPackage scannedPackage);
 
     /**
      * The native library paths and related properties that should be set on a
-     * {@link android.content.pm.PackageParser.Package}.
+     * {@link ParsedPackage}.
      */
     final class NativeLibraryPaths {
         public final String nativeLibraryRootDir;
@@ -91,11 +92,11 @@
             this.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
         }
 
-        public void applyTo(PackageParser.Package pkg) {
-            pkg.applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir;
-            pkg.applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
-            pkg.applicationInfo.nativeLibraryDir = nativeLibraryDir;
-            pkg.applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+        public void applyTo(ParsedPackage pkg) {
+            pkg.setNativeLibraryRootDir(nativeLibraryRootDir)
+                    .setNativeLibraryRootRequiresIsa(nativeLibraryRootRequiresIsa)
+                    .setNativeLibraryDir(nativeLibraryDir)
+                    .setSecondaryNativeLibraryDir(secondaryNativeLibraryDir);
         }
     }
 
@@ -112,13 +113,13 @@
             this.secondary = secondary;
         }
 
-        Abis(PackageParser.Package pkg) {
-            this(pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi);
+        Abis(AndroidPackage pkg) {
+            this(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi());
         }
 
-        public void applyTo(PackageParser.Package pkg) {
-            pkg.applicationInfo.primaryCpuAbi = primary;
-            pkg.applicationInfo.secondaryCpuAbi = secondary;
+        public void applyTo(ParsedPackage pkg) {
+            pkg.setPrimaryCpuAbi(primary)
+                    .setSecondaryCpuAbi(secondary);
         }
         public void applyTo(PackageSetting pkgSetting) {
             // pkgSetting might be null during rescan following uninstall of updates
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 1d3d24c..6de5203 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -28,7 +28,7 @@
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -122,10 +122,10 @@
 
     @Override
     public NativeLibraryPaths getNativeLibraryPaths(
-            PackageParser.Package pkg, File appLib32InstallDir) {
-        return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath,
-                pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(),
-                pkg.applicationInfo.isUpdatedSystemApp());
+            AndroidPackage pkg, File appLib32InstallDir) {
+        return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.getCodePath(),
+                pkg.getBaseCodePath(), pkg.isSystemApp(),
+                pkg.isUpdatedSystemApp());
     }
 
     private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis,
@@ -192,12 +192,12 @@
     }
 
     @Override
-    public Abis getBundledAppAbis(PackageParser.Package pkg) {
-        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
+    public Abis getBundledAppAbis(AndroidPackage pkg) {
+        final String apkName = deriveCodePathName(pkg.getCodePath());
 
         // If "/system/lib64/apkname" exists, assume that is the per-package
         // native library directory to use; otherwise use "/system/lib/apkname".
-        final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
+        final String apkRoot = calculateBundledApkRoot(pkg.getBaseCodePath());
         final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName);
         return abis;
     }
@@ -210,8 +210,8 @@
      *                {@code /oem} under which system libraries are installed.
      * @param apkName the name of the installed package.
      */
-    private Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
-        final File codeFile = new File(pkg.codePath);
+    private Abis getBundledAppAbi(AndroidPackage pkg, String apkRoot, String apkName) {
+        final File codeFile = new File(pkg.getCodePath());
 
         final boolean has64BitLibs;
         final boolean has32BitLibs;
@@ -263,7 +263,7 @@
             // ABI that's higher on the list, i.e, a device that's configured to prefer
             // 64 bit apps will see a 64 bit primary ABI,
 
-            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) == 0) {
                 Slog.e(PackageManagerService.TAG,
                         "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
             }
@@ -284,14 +284,14 @@
 
     @Override
     public Pair<Abis, NativeLibraryPaths> derivePackageAbi(
-            PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs)
+            AndroidPackage pkg, String cpuAbiOverride, boolean extractLibs)
             throws PackageManagerException {
         // Give ourselves some initial paths; we'll come back for another
         // pass once we've determined ABI below.
         final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg),
-                PackageManagerService.sAppLib32InstallDir, pkg.codePath,
-                pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(),
-                pkg.applicationInfo.isUpdatedSystemApp());
+                PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(),
+                pkg.getBaseCodePath(), pkg.isSystemApp(),
+                pkg.isUpdatedSystemApp());
 
         // We shouldn't attempt to extract libs from system app when it was not updated.
         if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
@@ -318,12 +318,13 @@
             // Null out the abis so that they can be recalculated.
             primaryCpuAbi = null;
             secondaryCpuAbi = null;
-            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) != 0) {
                 // Warn if we've set an abiOverride for multi-lib packages..
                 // By definition, we need to copy both 32 and 64 bit libraries for
                 // such packages.
-                if (pkg.cpuAbiOverride != null
-                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+                if (pkg.getCpuAbiOverride() != null
+                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(
+                        pkg.getCpuAbiOverride())) {
                     Slog.w(PackageManagerService.TAG,
                             "Ignoring abiOverride for multi arch application.");
                 }
@@ -382,7 +383,7 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        if (pkg.use32bitAbi) {
+                        if (pkg.isUse32BitAbi()) {
                             secondaryCpuAbi = primaryCpuAbi;
                             primaryCpuAbi = abi;
                         } else {
@@ -449,9 +450,9 @@
         final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
         return new Pair<>(abis,
                 getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
-                        pkg.codePath, pkg.applicationInfo.sourceDir,
-                        pkg.applicationInfo.isSystemApp(),
-                        pkg.applicationInfo.isUpdatedSystemApp()));
+                        pkg.getCodePath(), pkg.getBaseCodePath(),
+                        pkg.isSystemApp(),
+                        pkg.isUpdatedSystemApp()));
     }
 
     /**
@@ -470,11 +471,11 @@
     @Override
     @Nullable
     public String getAdjustedAbiForSharedUser(
-            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
+            Set<PackageSetting> packagesForUser, AndroidPackage scannedPackage) {
         String requiredInstructionSet = null;
-        if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
+        if (scannedPackage != null && scannedPackage.getPrimaryCpuAbi() != null) {
             requiredInstructionSet = VMRuntime.getInstructionSet(
-                    scannedPackage.applicationInfo.primaryCpuAbi);
+                    scannedPackage.getPrimaryCpuAbi());
         }
 
         PackageSetting requirer = null;
@@ -483,7 +484,7 @@
             // when scannedPackage is an update of an existing package. Without this check,
             // we will never be able to change the ABI of any package belonging to a shared
             // user, even if it's compatible with other packages.
-            if (scannedPackage != null && scannedPackage.packageName.equals(ps.name)) {
+            if (scannedPackage != null && scannedPackage.getPackageName().equals(ps.name)) {
                 continue;
             }
             if (ps.primaryCpuAbiString == null) {
@@ -521,7 +522,7 @@
         } else {
             // requirer == null implies that we're updating all ABIs in the set to
             // match scannedPackage.
-            adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi;
+            adjustedAbi = scannedPackage.getPrimaryCpuAbi();
         }
         return adjustedAbi;
     }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4f7c8c8..2b42221 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -41,10 +41,10 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -53,6 +53,7 @@
 import android.os.WorkSource;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
@@ -109,9 +110,9 @@
         this.mSystemReady = from.mSystemReady;
     }
 
-    static boolean canOptimizePackage(PackageParser.Package pkg) {
+    static boolean canOptimizePackage(AndroidPackage pkg) {
         // We do not dexopt a package with no code.
-        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
+        if ((pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) == 0) {
             return false;
         }
 
@@ -125,18 +126,18 @@
      * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
      * synchronized on {@link #mInstallLock}.
      */
-    int performDexOpt(PackageParser.Package pkg,
+    int performDexOpt(AndroidPackage pkg,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
-        if (pkg.applicationInfo.uid == -1) {
-            throw new IllegalArgumentException("Dexopt for " + pkg.packageName
+        if (pkg.getUid() == -1) {
+            throw new IllegalArgumentException("Dexopt for " + pkg.getPackageName()
                     + " has invalid uid.");
         }
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
         synchronized (mInstallLock) {
-            final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
+            final long acquireTime = acquireWakeLockLI(pkg.getUid());
             try {
                 return performDexOptLI(pkg, instructionSets,
                         packageStats, packageUseInfo, options);
@@ -151,19 +152,20 @@
      * It assumes the install lock is held.
      */
     @GuardedBy("mInstallLock")
-    private int performDexOptLI(PackageParser.Package pkg,
+    private int performDexOptLI(AndroidPackage pkg,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
-        final List<SharedLibraryInfo> sharedLibraries = pkg.usesLibraryInfos;
+        final List<SharedLibraryInfo> sharedLibraries = pkg.getUsesLibraryInfos();
         final String[] instructionSets = targetInstructionSets != null ?
-                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
+                targetInstructionSets : getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
+                pkg.getSecondaryCpuAbi());
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         final List<String> paths = pkg.getAllCodePaths();
 
-        int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+        int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
         if (sharedGid == -1) {
-            Slog.wtf(TAG, "Well this is awkward; package " + pkg.applicationInfo.name + " had UID "
-                    + pkg.applicationInfo.uid, new Throwable());
+            Slog.wtf(TAG, "Well this is awkward; package " + pkg.getAppInfoName() + " had UID "
+                    + pkg.getUid(), new Throwable());
             sharedGid = android.os.Process.NOBODY_UID;
         }
 
@@ -171,21 +173,21 @@
         // For each code path in the package, this array contains the class loader context that
         // needs to be passed to dexopt in order to ensure correct optimizations.
         boolean[] pathsWithCode = new boolean[paths.size()];
-        pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+        pathsWithCode[0] = (pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0;
         for (int i = 1; i < paths.size(); i++) {
-            pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+            pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
         }
         String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
-                pkg.applicationInfo, sharedLibraries, pathsWithCode);
+                pkg, sharedLibraries, pathsWithCode);
 
         // Sanity check that we do not call dexopt with inconsistent data.
         if (paths.size() != classLoaderContexts.length) {
-            String[] splitCodePaths = pkg.applicationInfo.getSplitCodePaths();
+            String[] splitCodePaths = pkg.getSplitCodePaths();
             throw new IllegalStateException("Inconsistent information "
                 + "between PackageParser.Package and its ApplicationInfo. "
                 + "pkg.getAllCodePaths=" + paths
-                + " pkg.applicationInfo.getBaseCodePath=" + pkg.applicationInfo.getBaseCodePath()
-                + " pkg.applicationInfo.getSplitCodePaths="
+                + " pkg.getBaseCodePath=" + pkg.getBaseCodePath()
+                + " pkg.getSplitCodePaths="
                 + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
         }
 
@@ -211,7 +213,8 @@
                 }
             }
 
-            String profileName = ArtManager.getProfileName(i == 0 ? null : pkg.splitNames[i - 1]);
+            String profileName = ArtManager.getProfileName(
+                    i == 0 ? null : pkg.getSplitNames()[i - 1]);
 
             String dexMetadataPath = null;
             if (options.isDexoptInstallWithDexMetadata()) {
@@ -222,7 +225,7 @@
 
             final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                     || packageUseInfo.isUsedByOtherApps(path);
-            final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+            final String compilerFilter = getRealCompilerFilter(pkg,
                 options.getCompilerFilter(), isUsedByOtherApps);
             final boolean profileUpdated = options.isCheckForProfileUpdates() &&
                 isProfileUpdated(pkg, sharedGid, profileName, compilerFilter);
@@ -257,7 +260,7 @@
      *      DEX_OPT_SKIPPED if the path does not need to be deopt-ed.
      */
     @GuardedBy("mInstallLock")
-    private int dexOptPath(PackageParser.Package pkg, String path, String isa,
+    private int dexOptPath(AndroidPackage pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String classLoaderContext,
             int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
             String profileName, String dexMetadataPath, int compilationReason) {
@@ -270,7 +273,7 @@
         String oatDir = getPackageOatDirIfSupported(pkg);
 
         Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
-                + " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
+                + " pkg=" + pkg.getAppInfoPackageName() + " isa=" + isa
                 + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
                 + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
                 + " classLoaderContext=" + classLoaderContext);
@@ -281,9 +284,9 @@
             // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
             // installd only uses downgrade flag for secondary dex files and ignores it for
             // primary dex files.
-            mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
-                    compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
-                    false /* downgrade*/, pkg.applicationInfo.targetSdkVersion,
+            mInstaller.dexopt(path, uid, pkg.getPackageName(), isa, dexoptNeeded, oatDir,
+                    dexoptFlags, compilerFilter, pkg.getVolumeUuid(), classLoaderContext,
+                    pkg.getSeInfo(), false /* downgrade*/, pkg.getTargetSdkVersion(),
                     profileName, dexMetadataPath,
                     getAugmentedReasonName(compilationReason, dexMetadataPath != null));
 
@@ -446,9 +449,10 @@
     /**
      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
      */
-    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
+    void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
             PackageDexUsage.PackageUseInfo useInfo) {
-        final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+        final String[] instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
+                pkg.getSecondaryCpuAbi());
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
 
         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
@@ -504,7 +508,7 @@
         // When an app or priv app is configured to run out of box, only verify it.
         if (info.isEmbeddedDexUsed()
                 || (info.isPrivilegedApp()
-                    && DexManager.isPackageSelectedToRunOob(info.packageName))) {
+                && DexManager.isPackageSelectedToRunOob(info.packageName))) {
             return "verify";
         }
 
@@ -535,12 +539,43 @@
     }
 
     /**
-     * Computes the dex flags that needs to be pass to installd for the given package and compiler
-     * filter.
+     * Returns the compiler filter that should be used to optimize the package code.
+     * The target filter will be updated if the package code is used by other apps
+     * or if it has the safe mode flag set.
      */
-    private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
-            DexoptOptions options) {
-        return getDexFlags(pkg.applicationInfo, compilerFilter, options);
+    private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter,
+            boolean isUsedByOtherApps) {
+        // When an app or priv app is configured to run out of box, only verify it.
+        if (pkg.isEmbeddedDexUsed()
+                || (pkg.isPrivileged()
+                    && DexManager.isPackageSelectedToRunOob(pkg.getPackageName()))) {
+            return "verify";
+        }
+
+        // We force vmSafeMode on debuggable apps as well:
+        //  - the runtime ignores their compiled code
+        //  - they generally have lots of methods that could make the compiler used run
+        //    out of memory (b/130828957)
+        // Note that forcing the compiler filter here applies to all compilations (even if they
+        // are done via adb shell commands). That's ok because right now the runtime will ignore
+        // the compiled code anyway. The alternative would have been to update either
+        // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
+        // but that would have the downside of possibly producing a big odex files which would
+        // be ignored anyway.
+        boolean vmSafeModeOrDebuggable = ((pkg.getFlags() & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0)
+                || ((pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+
+        if (vmSafeModeOrDebuggable) {
+            return getSafeModeCompilerFilter(targetCompilerFilter);
+        }
+
+        if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
+            // If the dex files is used by other apps, apply the shared filter.
+            return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+                    PackageManagerService.REASON_SHARED);
+        }
+
+        return targetCompilerFilter;
     }
 
     private boolean isAppImageEnabled() {
@@ -548,7 +583,24 @@
     }
 
     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
-        int flags = info.flags;
+        return getDexFlags(info.flags, info.getHiddenApiEnforcementPolicy(),
+                info.splitDependencies, info.requestsIsolatedSplitLoading(), compilerFilter,
+                options);
+    }
+    private int getDexFlags(AndroidPackage pkg, String compilerFilter,
+            DexoptOptions options) {
+        return getDexFlags(pkg.getFlags(), pkg.getHiddenApiEnforcementPolicy(),
+                pkg.getSplitDependencies(), pkg.requestsIsolatedSplitLoading(), compilerFilter,
+                options);
+    }
+
+    /**
+     * Computes the dex flags that needs to be pass to installd for the given package and compiler
+     * filter.
+     */
+    private int getDexFlags(int flags, int hiddenApiEnforcementPolicy,
+            SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
+            String compilerFilter, DexoptOptions options) {
         boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         // Profile guide compiled oat files should not be public unles they are based
         // on profiles from dex metadata archives.
@@ -560,7 +612,9 @@
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
         // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
-        int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_DISABLED
+        // TODO(b/135203078): This flag is no longer set as part of AndroidPackage
+        //  and may not be preserved
+        int hiddenApiFlag = hiddenApiEnforcementPolicy == HIDDEN_API_ENFORCEMENT_DISABLED
                 ? 0
                 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
         // Avoid generating CompactDex for modes that are latency critical.
@@ -578,8 +632,8 @@
         // declare inter-split dependencies, then all the splits will be loaded in the base
         // apk class loader (in the order of their definition, otherwise disable app images
         // because they are unsupported for multiple class loaders. b/7269679
-        boolean generateAppImage = isProfileGuidedFilter && (info.splitDependencies == null ||
-                !info.requestsIsolatedSplitLoading()) && isAppImageEnabled();
+        boolean generateAppImage = isProfileGuidedFilter && (splitDependencies == null ||
+                !requestsIsolatedSplitLoading) && isAppImageEnabled();
         int dexFlags =
                 (isPublic ? DEXOPT_PUBLIC : 0)
                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -617,7 +671,7 @@
      * current profile and the reference profile will be merged and subsequent calls
      * may return a different result.
      */
-    private boolean isProfileUpdated(PackageParser.Package pkg, int uid, String profileName,
+    private boolean isProfileUpdated(AndroidPackage pkg, int uid, String profileName,
             String compilerFilter) {
         // Check if we are allowed to merge and if the compiler filter is profile guided.
         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
@@ -625,7 +679,7 @@
         }
         // Merge profiles. It returns whether or not there was an updated in the profile info.
         try {
-            return mInstaller.mergeProfiles(uid, pkg.packageName, profileName);
+            return mInstaller.mergeProfiles(uid, pkg.getPackageName(), profileName);
         } catch (InstallerException e) {
             Slog.w(TAG, "Failed to merge profiles", e);
         }
@@ -645,11 +699,11 @@
      * not needed or unsupported for the package.
      */
     @Nullable
-    private String getPackageOatDirIfSupported(PackageParser.Package pkg) {
+    private String getPackageOatDirIfSupported(AndroidPackage pkg) {
         if (!pkg.canHaveOatDir()) {
             return null;
         }
-        File codePath = new File(pkg.codePath);
+        File codePath = new File(pkg.getCodePath());
         if (!codePath.isDirectory()) {
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b720290..a34ca91 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -66,6 +66,7 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Binder;
@@ -1573,7 +1574,7 @@
         for (File addedFile : addedFiles) {
             final ApkLite apk;
             try {
-                apk = PackageParser.parseApkLite(
+                apk = ApkLiteParseUtils.parseApkLite(
                         addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
             } catch (PackageParserException e) {
                 throw PackageManagerException.from(e);
@@ -1672,7 +1673,7 @@
             ApplicationInfo appInfo = pkgInfo.applicationInfo;
             try {
                 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
-                existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
+                existingBase = ApkLiteParseUtils.parseApkLite(new File(appInfo.getBaseCodePath()),
                         PackageParser.PARSE_COLLECT_CERTIFICATES);
             } catch (PackageParserException e) {
                 throw PackageManagerException.from(e);
diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java
index 031b5ce..10685b0 100644
--- a/services/core/java/com/android/server/pm/PackageKeySetData.java
+++ b/services/core/java/com/android/server/pm/PackageKeySetData.java
@@ -20,6 +20,8 @@
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.Map;
+
 public class PackageKeySetData {
 
     static final long KEYSET_UNASSIGNED = -1;
@@ -90,16 +92,13 @@
     /*
      * Replace defined keysets with new ones.
      */
-    protected void setAliases(ArrayMap<String, Long> newAliases) {
+    protected void setAliases(Map<String, Long> newAliases) {
 
         /* remove old aliases */
         removeAllDefinedKeySets();
 
         /* add new ones */
-        final int newAliasSize = newAliases.size();
-        for (int i = 0; i < newAliasSize; i++) {
-            mKeySetAliases.put(newAliases.keyAt(i), newAliases.valueAt(i));;
-        }
+        mKeySetAliases.putAll(newAliases);
     }
 
     protected void addDefinedKeySet(long ks, String alias) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d07e2d2..f706dc3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -34,7 +34,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -158,7 +157,6 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ModuleInfo;
-import android.content.pm.PackageBackwardCompatibility;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
@@ -169,7 +167,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.ParseFlags;
@@ -194,6 +191,19 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.IArtManager;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.library.PackageBackwardCompatibility;
 import android.content.res.Resources;
 import android.content.rollback.IRollbackManager;
 import android.database.ContentObserver;
@@ -460,20 +470,19 @@
     static final int SCAN_REQUIRE_KNOWN = 1 << 7;
     static final int SCAN_MOVE = 1 << 8;
     static final int SCAN_INITIAL = 1 << 9;
-    static final int SCAN_CHECK_ONLY = 1 << 10;
-    static final int SCAN_DONT_KILL_APP = 1 << 11;
-    static final int SCAN_IGNORE_FROZEN = 1 << 12;
-    static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 13;
-    static final int SCAN_AS_INSTANT_APP = 1 << 14;
-    static final int SCAN_AS_FULL_APP = 1 << 15;
-    static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 16;
-    static final int SCAN_AS_SYSTEM = 1 << 17;
-    static final int SCAN_AS_PRIVILEGED = 1 << 18;
-    static final int SCAN_AS_OEM = 1 << 19;
-    static final int SCAN_AS_VENDOR = 1 << 20;
-    static final int SCAN_AS_PRODUCT = 1 << 21;
-    static final int SCAN_AS_SYSTEM_EXT = 1 << 22;
-    static final int SCAN_AS_ODM = 1 << 23;
+    static final int SCAN_DONT_KILL_APP = 1 << 10;
+    static final int SCAN_IGNORE_FROZEN = 1 << 11;
+    static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 12;
+    static final int SCAN_AS_INSTANT_APP = 1 << 13;
+    static final int SCAN_AS_FULL_APP = 1 << 14;
+    static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 15;
+    static final int SCAN_AS_SYSTEM = 1 << 16;
+    static final int SCAN_AS_PRIVILEGED = 1 << 17;
+    static final int SCAN_AS_OEM = 1 << 18;
+    static final int SCAN_AS_VENDOR = 1 << 19;
+    static final int SCAN_AS_PRODUCT = 1 << 20;
+    static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
+    static final int SCAN_AS_ODM = 1 << 22;
 
     @IntDef(flag = true, prefix = { "SCAN_" }, value = {
             SCAN_NO_DEX,
@@ -484,7 +493,6 @@
             SCAN_REQUIRE_KNOWN,
             SCAN_MOVE,
             SCAN_INITIAL,
-            SCAN_CHECK_ONLY,
             SCAN_DONT_KILL_APP,
             SCAN_IGNORE_FROZEN,
             SCAN_FIRST_BOOT_OR_UPGRADE,
@@ -603,11 +611,19 @@
     public static final int REASON_LAST = REASON_SHARED;
 
     /**
-     * Whether the package parser cache is enabled.
+     * The initial enabled state of the cache before other checks are done.
      */
     private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
 
     /**
+     * Whether to skip all other checks and force the cache to be enabled.
+     *
+     * Setting this to true will cause the cache to be named "debug" to avoid eviction from
+     * build fingerprint changes.
+     */
+    private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
+
+    /**
      * Permissions required in order to receive instant application lifecycle broadcasts.
      */
     private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
@@ -664,7 +680,7 @@
 
     // Keys are String (package name), values are Package.
     @GuardedBy("mLock")
-    final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
+    final ArrayMap<String, AndroidPackage> mPackages = new ArrayMap<>();
 
     // Keys are isolated uids and values are the uid of the application
     // that created the isolated proccess.
@@ -977,17 +993,17 @@
             return PackageManagerService.this.hasSystemFeature(feature, 0);
         }
 
-        final List<PackageParser.Package> getStaticOverlayPackages(
-                Collection<PackageParser.Package> allPackages, String targetPackageName) {
+        final List<AndroidPackage> getStaticOverlayPackages(
+                Collection<AndroidPackage> allPackages, String targetPackageName) {
             if ("android".equals(targetPackageName)) {
                 // Static RROs targeting to "android", ie framework-res.apk, are already applied by
                 // native AssetManager.
                 return null;
             }
 
-            List<PackageParser.Package> overlayPackages = null;
-            for (PackageParser.Package p : allPackages) {
-                if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
+            List<AndroidPackage> overlayPackages = null;
+            for (AndroidPackage p : allPackages) {
+                if (targetPackageName.equals(p.getOverlayTarget()) && p.isOverlayIsStatic()) {
                     if (overlayPackages == null) {
                         overlayPackages = new ArrayList<>();
                     }
@@ -995,25 +1011,25 @@
                 }
             }
             if (overlayPackages != null) {
-                Comparator<PackageParser.Package> cmp =
-                        Comparator.comparingInt(p -> p.mOverlayPriority);
+                Comparator<AndroidPackage> cmp =
+                        Comparator.comparingInt(p -> p.getOverlayPriority());
                 overlayPackages.sort(cmp);
             }
             return overlayPackages;
         }
 
-        final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
+        final String[] getStaticOverlayPaths(List<AndroidPackage> overlayPackages,
                 String targetPath) {
             if (overlayPackages == null || overlayPackages.isEmpty()) {
                 return null;
             }
             List<String> overlayPathList = null;
-            for (PackageParser.Package overlayPackage : overlayPackages) {
+            for (AndroidPackage overlayPackage : overlayPackages) {
                 if (targetPath == null) {
                     if (overlayPathList == null) {
                         overlayPathList = new ArrayList<>();
                     }
-                    overlayPathList.add(overlayPackage.baseCodePath);
+                    overlayPathList.add(overlayPackage.getBaseCodePath());
                     continue;
                 }
 
@@ -1023,23 +1039,23 @@
                     //
                     // OverlayManagerService will update each of them with a correct gid from its
                     // target package app id.
-                    mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
+                    mInstaller.idmap(targetPath, overlayPackage.getBaseCodePath(),
                             UserHandle.getSharedAppGid(
                                     UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
                     if (overlayPathList == null) {
                         overlayPathList = new ArrayList<>();
                     }
-                    overlayPathList.add(overlayPackage.baseCodePath);
+                    overlayPathList.add(overlayPackage.getBaseCodePath());
                 } catch (InstallerException e) {
                     Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
-                            overlayPackage.baseCodePath);
+                            overlayPackage.getBaseCodePath());
                 }
             }
             return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
         }
 
         String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            List<PackageParser.Package> overlayPackages;
+            List<AndroidPackage> overlayPackages;
             synchronized (mInstallLock) {
                 synchronized (mLock) {
                     overlayPackages = getStaticOverlayPackages(
@@ -1062,12 +1078,12 @@
     }
 
     class ParallelPackageParserCallback extends PackageParserCallback {
-        List<PackageParser.Package> mOverlayPackages = null;
+        List<AndroidPackage> mOverlayPackages = null;
 
         void findStaticOverlayPackages() {
             synchronized (mLock) {
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mOverlayIsStatic) {
+                for (AndroidPackage p : mPackages.values()) {
+                    if (p.isOverlayIsStatic()) {
                         if (mOverlayPackages == null) {
                             mOverlayPackages = new ArrayList<>();
                         }
@@ -1101,7 +1117,7 @@
             new ArrayMap<>();
 
     // Mapping from instrumentation class names to info about them.
-    final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
+    final ArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
             new ArrayMap<>();
 
     // Packages whose data we have transfered into another package, thus
@@ -1150,13 +1166,13 @@
     final ActivityInfo mResolveActivity = new ActivityInfo();
     final ResolveInfo mResolveInfo = new ResolveInfo();
     ComponentName mResolveComponentName;
-    PackageParser.Package mPlatformPackage;
+    AndroidPackage mPlatformPackage;
     ComponentName mCustomResolverComponentName;
 
     boolean mResolverReplaced = false;
 
     private final @Nullable ComponentName mIntentFilterVerifierComponent;
-    private final @Nullable IntentFilterVerifier<ActivityIntentInfo> mIntentFilterVerifier;
+    private final @Nullable IntentFilterVerifier<ParsedActivityIntentInfo> mIntentFilterVerifier;
 
     private int mIntentFilterVerificationToken = 0;
 
@@ -1189,14 +1205,19 @@
     private Future<?> mPrepareAppDataFuture;
 
     private static class IFVerificationParams {
-        PackageParser.Package pkg;
+        String packageName;
+        boolean hasDomainUrls;
+        List<ParsedActivity> activities;
         boolean replacing;
         int userId;
         int verifierUid;
 
-        public IFVerificationParams(PackageParser.Package _pkg, boolean _replacing,
+        public IFVerificationParams(String packageName, boolean hasDomainUrls,
+                List<ParsedActivity> activities, boolean _replacing,
                 int _userId, int _verifierUid) {
-            pkg = _pkg;
+            this.packageName = packageName;
+            this.hasDomainUrls = hasDomainUrls;
+            this.activities = activities;
             replacing = _replacing;
             userId = _userId;
             verifierUid = _verifierUid;
@@ -1210,7 +1231,7 @@
         void receiveVerificationResponse(int verificationId);
     }
 
-    private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
+    private class IntentVerifierProxy implements IntentFilterVerifier<ParsedActivityIntentInfo> {
         private Context mContext;
         private ComponentName mIntentFilterVerifierComponent;
         private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
@@ -1235,11 +1256,11 @@
 
                 String packageName = ivs.getPackageName();
 
-                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+                ArrayList<ParsedActivityIntentInfo> filters = ivs.getFilters();
                 final int filterCount = filters.size();
                 ArraySet<String> domainsSet = new ArraySet<>();
                 for (int m=0; m<filterCount; m++) {
-                    PackageParser.ActivityIntentInfo filter = filters.get(m);
+                    ParsedActivityIntentInfo filter = filters.get(m);
                     domainsSet.addAll(filter.getHostsList());
                 }
                 synchronized (mLock) {
@@ -1291,14 +1312,14 @@
 
             final boolean verified = ivs.isVerified();
 
-            ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+            ArrayList<ParsedActivityIntentInfo> filters = ivs.getFilters();
             final int count = filters.size();
             if (DEBUG_DOMAIN_VERIFICATION) {
                 Slog.i(TAG, "Received verification response " + verificationId
                         + " for " + count + " filters, verified=" + verified);
             }
             for (int n=0; n<count; n++) {
-                PackageParser.ActivityIntentInfo filter = filters.get(n);
+                ParsedActivityIntentInfo filter = filters.get(n);
                 filter.setVerified(verified);
 
                 if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
@@ -1411,7 +1432,7 @@
 
         @Override
         public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId,
-                    ActivityIntentInfo filter, String packageName) {
+                ParsedActivityIntentInfo filter, String packageName) {
             if (!hasValidDomains(filter)) {
                 return false;
             }
@@ -1440,7 +1461,7 @@
         }
     }
 
-    private static boolean hasValidDomains(ActivityIntentInfo filter) {
+    private static boolean hasValidDomains(ParsedActivityIntentInfo filter) {
         return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
                 && (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
                         filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
@@ -1705,7 +1726,7 @@
                         final List<String> whitelistedRestrictedPermissions = ((args.installFlags
                                 & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0
                                     && parentRes.pkg != null)
-                                ? parentRes.pkg.requestedPermissions
+                                ? parentRes.pkg.getRequestedPermissions()
                                 : args.whitelistedRestrictedPermissions;
 
                         // Handle the parent package
@@ -1851,8 +1872,8 @@
                 }
                 case START_INTENT_FILTER_VERIFICATIONS: {
                     IFVerificationParams params = (IFVerificationParams) msg.obj;
-                    verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
-                            params.replacing, params.pkg);
+                    verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
+                            params.packageName, params.hasDomainUrls, params.activities);
                     break;
                 }
                 case INTENT_FILTER_VERIFIED: {
@@ -2002,20 +2023,11 @@
                                     ? res.removedInfo.installerPackageName
                                     : null;
 
-            // If this is the first time we have child packages for a disabled privileged
-            // app that had no children, we grant requested runtime permissions to the new
-            // children if the parent on the system image had them already granted.
-            if (res.pkg.parentPackage != null) {
-                final int callingUid = Binder.getCallingUid();
-                mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage(
-                        res.pkg, callingUid);
-            }
-
             synchronized (mLock) {
                 mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
             }
 
-            final String packageName = res.pkg.applicationInfo.packageName;
+            final String packageName = res.pkg.getAppInfoPackageName();
 
             // Determine the set of users who are adding this package for
             // the first time vs. those who are seeing an update.
@@ -2024,7 +2036,7 @@
             int[] updateUserIds = EMPTY_INT_ARRAY;
             int[] instantUserIds = EMPTY_INT_ARRAY;
             final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
-            final PackageSetting ps = (PackageSetting) res.pkg.mExtras;
+            final PackageSetting ps = getPackageSetting(res.pkg.getPackageName());
             for (int newUser : res.newUsers) {
                 final boolean isInstantApp = ps.getInstantApp(newUser);
                 if (allNewUsers) {
@@ -2058,13 +2070,14 @@
             }
 
             // Send installed broadcasts if the package is not a static shared lib.
-            if (res.pkg.staticSharedLibName == null) {
-                mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
+            if (res.pkg.getStaticSharedLibName() == null) {
+                mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(
+                        res.pkg.getBaseCodePath());
 
                 // Send added for users that see the package for the first time
                 // sendPackageAddedForNewUsers also deals with system apps
                 int appId = UserHandle.getAppId(res.uid);
-                boolean isSystem = res.pkg.applicationInfo.isSystemApp();
+                boolean isSystem = res.pkg.isSystemApp();
                 sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
                         virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds);
 
@@ -2142,30 +2155,30 @@
                         final StorageManager storage = mInjector.getStorageManager();
                         VolumeInfo volume =
                                 storage.findVolumeByUuid(
-                                        res.pkg.applicationInfo.storageUuid.toString());
+                                        res.pkg.getStorageUuid().toString());
                         int packageExternalStorageType =
                                 getPackageExternalStorageType(volume, isExternal(res.pkg));
                         // If the package was installed externally, log it.
                         if (packageExternalStorageType != StorageEnums.UNKNOWN) {
                             StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
-                                    packageExternalStorageType, res.pkg.packageName);
+                                    packageExternalStorageType, res.pkg.getPackageName());
                         }
                     }
                     if (DEBUG_INSTALL) {
                         Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
                     }
-                    final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
+                    final int[] uidArray = new int[]{res.pkg.getUid()};
                     ArrayList<String> pkgList = new ArrayList<>(1);
                     pkgList.add(packageName);
                     sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
                 }
             } else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
                 for (int i = 0; i < res.libraryConsumers.size(); i++) {
-                    PackageParser.Package pkg = res.libraryConsumers.get(i);
+                    AndroidPackage pkg = res.libraryConsumers.get(i);
                     // send broadcast that all consumers of the static shared library have changed
-                    sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/,
-                            new ArrayList<>(Collections.singletonList(pkg.packageName)),
-                            pkg.applicationInfo.uid);
+                    sendPackageChangedBroadcast(pkg.getPackageName(), false /*killFlag*/,
+                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+                            pkg.getUid());
                 }
             }
 
@@ -2295,7 +2308,7 @@
 
     private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
             IPackageInstallObserver2 observer) {
-        String packageName = info.pkg.packageName;
+        String packageName = info.pkg.getPackageName();
         mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
         Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
         mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
@@ -2833,11 +2846,11 @@
             final List<String> stubSystemApps = new ArrayList<>();
             if (!mOnlyCore) {
                 // do this first before mucking with mPackages for the "expecting better" case
-                final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
+                final Iterator<AndroidPackage> pkgIterator = mPackages.values().iterator();
                 while (pkgIterator.hasNext()) {
-                    final PackageParser.Package pkg = pkgIterator.next();
-                    if (pkg.isStub) {
-                        stubSystemApps.add(pkg.packageName);
+                    final AndroidPackage pkg = pkgIterator.next();
+                    if (pkg.isStub()) {
+                        stubSystemApps.add(pkg.getPackageName());
                     }
                 }
 
@@ -2856,7 +2869,7 @@
                     /*
                      * If the package is scanned, it's not erased.
                      */
-                    final PackageParser.Package scannedPkg = mPackages.get(ps.name);
+                    final AndroidPackage scannedPkg = mPackages.get(ps.name);
                     if (scannedPkg != null) {
                         /*
                          * If the system app is both scanned and in the
@@ -2933,7 +2946,7 @@
                 // app completely. Otherwise, revoke their system privileges.
                 for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
                     final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
-                    final PackageParser.Package pkg = mPackages.get(packageName);
+                    final AndroidPackage pkg = mPackages.get(packageName);
                     final String msg;
 
                     // remove from the disabled system list; do this first so any future
@@ -2959,7 +2972,7 @@
                         // special privileges
                         removePackageLI(pkg, true);
                         try {
-                            final File codePath = new File(pkg.applicationInfo.getCodePath());
+                            final File codePath = new File(pkg.getAppInfoCodePath());
                             scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
                         } catch (PackageManagerException e) {
                             Slog.e(TAG, "Failed to parse updated, ex-system package: "
@@ -3154,7 +3167,7 @@
                 }
                 int count = 0;
                 for (String pkgName : deferPackages) {
-                    PackageParser.Package pkg = null;
+                    AndroidPackage pkg = null;
                     synchronized (mLock) {
                         PackageSetting ps = mSettings.getPackageLPr(pkgName);
                         if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
@@ -3254,12 +3267,12 @@
 
             // Initialize InstantAppRegistry's Instant App list for all users.
             final int[] userIds = UserManagerService.getInstance().getUserIds();
-            for (PackageParser.Package pkg : mPackages.values()) {
+            for (AndroidPackage pkg : mPackages.values()) {
                 if (pkg.isSystem()) {
                     continue;
                 }
                 for (int userId : userIds) {
-                    final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                    final PackageSetting ps = getPackageSetting(pkg.getPackageName());
                     if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
                         continue;
                     }
@@ -3346,7 +3359,7 @@
                 continue;
             }
             // skip if the package isn't installed (?!); this should never happen
-            final PackageParser.Package pkg = mPackages.get(packageName);
+            final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null) {
                 systemStubPackageNames.remove(i);
                 continue;
@@ -3390,43 +3403,46 @@
      * APK will be installed and the package will be disabled. To recover from this situation,
      * the user will need to go into system settings and re-enable the package.
      */
-    private boolean enableCompressedPackage(PackageParser.Package stubPkg) {
+    private boolean enableCompressedPackage(AndroidPackage stubPkg) {
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE;
         synchronized (mInstallLock) {
-            final PackageParser.Package pkg;
+            final AndroidPackage pkg;
             try (PackageFreezer freezer =
-                    freezePackage(stubPkg.packageName, "setEnabledSetting")) {
+                    freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
                 pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
                 synchronized (mLock) {
                     prepareAppDataAfterInstallLIF(pkg);
                     try {
-                        updateSharedLibrariesLocked(pkg, null, mPackages);
+                        updateSharedLibrariesLocked(pkg, null,
+                                Collections.unmodifiableMap(mPackages));
                     } catch (PackageManagerException e) {
                         Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
                     }
-                    mPermissionManager.updatePermissions(pkg.packageName, pkg);
+                    mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
                     mSettings.writeLPr();
                 }
             } catch (PackageManagerException e) {
                 // Whoops! Something went very wrong; roll back to the stub and disable the package
                 try (PackageFreezer freezer =
-                        freezePackage(stubPkg.packageName, "setEnabledSetting")) {
+                        freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
                     synchronized (mLock) {
                         // NOTE: Ensure the system package is enabled; even for a compressed stub.
                         // If we don't, installing the system package fails during scan
                         enableSystemPackageLPw(stubPkg);
                     }
-                    installPackageFromSystemLIF(stubPkg.codePath,
+                    installPackageFromSystemLIF(stubPkg.getCodePath(),
                             null /*allUserHandles*/, null /*origUserHandles*/,
                             null /*origPermissionsState*/, true /*writeSettings*/);
                 } catch (PackageManagerException pme) {
                     // Serious WTF; we have to be able to install the stub
-                    Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme);
+                    Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
+                            pme);
                 } finally {
                     // Disable the package; the stub by itself is not runnable
                     synchronized (mLock) {
-                        final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
+                        final PackageSetting stubPs = mSettings.mPackages.get(
+                                stubPkg.getPackageName());
                         if (stubPs != null) {
                             stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
                                     UserHandle.USER_SYSTEM, "android");
@@ -3438,31 +3454,33 @@
             }
             clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                     | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
-            mDexManager.notifyPackageUpdated(pkg.packageName,
-                    pkg.baseCodePath, pkg.splitCodePaths);
+            mDexManager.notifyPackageUpdated(pkg.getPackageName(),
+                    pkg.getBaseCodePath(), pkg.getSplitCodePaths());
         }
         return true;
     }
 
-    private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg,
+    private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
             @ParseFlags int parseFlags, @ScanFlags int scanFlags)
                     throws PackageManagerException {
         if (DEBUG_COMPRESSION) {
-            Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName);
+            Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
         }
         // uncompress the binary to its eventual destination on /data
-        final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath);
+        final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getCodePath());
         if (scanFile == null) {
-            throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath);
+            throw new PackageManagerException(
+                    "Unable to decompress stub at " + stubPkg.getCodePath());
         }
         synchronized (mLock) {
-            mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/);
+            mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
         }
         removePackageLI(stubPkg, true /*chatty*/);
         try {
             return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
         } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e);
+            Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
+                    e);
             // Remove the failed install
             removeCodePathLI(scanFile);
             throw e;
@@ -3545,18 +3563,20 @@
     }
 
     private static @Nullable File preparePackageParserCache() {
-        if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
-            return null;
-        }
+        if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) {
+            if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
+                return null;
+            }
 
-        // Disable package parsing on eng builds to allow for faster incremental development.
-        if (Build.IS_ENG) {
-            return null;
-        }
+            // Disable package parsing on eng builds to allow for faster incremental development.
+            if (Build.IS_ENG) {
+                return null;
+            }
 
-        if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
-            Slog.i(TAG, "Disabling package parser cache due to system property.");
-            return null;
+            if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
+                Slog.i(TAG, "Disabling package parser cache due to system property.");
+                return null;
+            }
         }
 
         // The base directory for the package parser cache lives under /data/system/.
@@ -3568,10 +3588,12 @@
         // There are several items that need to be combined together to safely
         // identify cached items. In particular, changing the value of certain
         // feature flags should cause us to invalidate any caches.
-        final String cacheName = SystemProperties.digestOf(
-                "ro.build.fingerprint",
-                StorageManager.PROP_ISOLATED_STORAGE,
-                StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT);
+        final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
+                : SystemProperties.digestOf(
+                        "ro.build.fingerprint",
+                        StorageManager.PROP_ISOLATED_STORAGE,
+                        StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT
+                );
 
         // Reconcile cache directories, keeping only what we'd actually use.
         for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -3895,7 +3917,7 @@
         ArraySet<String> packages = systemConfig.getLinkedApps();
 
         for (String packageName : packages) {
-            PackageParser.Package pkg = mPackages.get(packageName);
+            AndroidPackage pkg = mPackages.get(packageName);
             if (pkg != null) {
                 if (!pkg.isSystem()) {
                     Slog.w(TAG, "Non-system app '" + packageName + "' in sysconfig <app-link>");
@@ -3903,13 +3925,15 @@
                 }
 
                 ArraySet<String> domains = null;
-                for (PackageParser.Activity a : pkg.activities) {
-                    for (ActivityIntentInfo filter : a.intents) {
-                        if (hasValidDomains(filter)) {
-                            if (domains == null) {
-                                domains = new ArraySet<>();
+                if (pkg.getActivities() != null) {
+                    for (ParsedActivity a : pkg.getActivities()) {
+                        for (ParsedActivityIntentInfo filter : a.intents) {
+                            if (hasValidDomains(filter)) {
+                                if (domains == null) {
+                                    domains = new ArraySet<>();
+                                }
+                                domains.addAll(filter.getHostsList());
                             }
-                            domains.addAll(filter.getHostsList());
                         }
                     }
                 }
@@ -4025,7 +4049,7 @@
         }
 
         final PackageUserState state = ps.readUserState(userId);
-        PackageParser.Package p = ps.pkg;
+        AndroidPackage p = ps.pkg;
         if (p != null) {
             final PermissionsState permissionsState = ps.getPermissionsState();
 
@@ -4033,10 +4057,10 @@
             final int[] gids = (flags & PackageManager.GET_GIDS) == 0
                     ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
             // Compute granted permissions only if package has requested permissions
-            final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+            final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
                     ? Collections.emptySet() : permissionsState.getPermissions(userId);
 
-            PackageInfo packageInfo = PackageParser.generatePackageInfo(p, gids, flags,
+            PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
                     ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
 
             if (packageInfo == null) {
@@ -4099,7 +4123,7 @@
                 throw new SecurityException("Package " + packageName + " is currently frozen!");
             }
 
-            if (!userKeyUnlocked && !ps.pkg.applicationInfo.isEncryptionAware()) {
+            if (!userKeyUnlocked && !ps.pkg.isEncryptionAware()) {
                 throw new SecurityException("Package " + packageName + " is not encryption aware!");
             }
         }
@@ -4112,9 +4136,9 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "is package available");
         synchronized (mLock) {
-            PackageParser.Package p = mPackages.get(packageName);
+            AndroidPackage p = mPackages.get(packageName);
             if (p != null) {
-                final PackageSetting ps = (PackageSetting) p.mExtras;
+                final PackageSetting ps = getPackageSetting(p.getPackageName());
                 if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return false;
                 }
@@ -4179,21 +4203,22 @@
                 }
             }
 
-            PackageParser.Package p = mPackages.get(packageName);
+            AndroidPackage p = mPackages.get(packageName);
             if (matchFactoryOnly && p != null && !isSystemApp(p)) {
                 return null;
             }
             if (DEBUG_PACKAGE_INFO)
                 Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
             if (p != null) {
-                final PackageSetting ps = (PackageSetting) p.mExtras;
+                final PackageSetting ps = getPackageSetting(p.getPackageName());
                 if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                     return null;
                 }
                 if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                     return null;
                 }
-                return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
+
+                return generatePackageInfo(ps, flags, userId);
             }
             if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -4229,34 +4254,34 @@
     private boolean isComponentVisibleToInstantApp(
             @Nullable ComponentName component, @ComponentType int type) {
         if (type == TYPE_ACTIVITY) {
-            final PackageParser.Activity activity = mComponentResolver.getActivity(component);
+            final ParsedActivity activity = mComponentResolver.getActivity(component);
             if (activity == null) {
                 return false;
             }
             final boolean visibleToInstantApp =
-                    (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+                    (activity.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
             final boolean explicitlyVisibleToInstantApp =
-                    (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
+                    (activity.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
             return visibleToInstantApp && explicitlyVisibleToInstantApp;
         } else if (type == TYPE_RECEIVER) {
-            final PackageParser.Activity activity = mComponentResolver.getReceiver(component);
+            final ParsedActivity activity = mComponentResolver.getReceiver(component);
             if (activity == null) {
                 return false;
             }
             final boolean visibleToInstantApp =
-                    (activity.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+                    (activity.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
             final boolean explicitlyVisibleToInstantApp =
-                    (activity.info.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
+                    (activity.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
             return visibleToInstantApp && !explicitlyVisibleToInstantApp;
         } else if (type == TYPE_SERVICE) {
-            final PackageParser.Service service = mComponentResolver.getService(component);
+            final ParsedService service = mComponentResolver.getService(component);
             return service != null
-                    ? (service.info.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
+                    ? (service.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
                     : false;
         } else if (type == TYPE_PROVIDER) {
-            final PackageParser.Provider provider = mComponentResolver.getProvider(component);
+            final ParsedProvider provider = mComponentResolver.getProvider(component);
             return provider != null
-                    ? (provider.info.flags & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
+                    ? (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
                     : false;
         } else if (type == TYPE_UNKNOWN) {
             return isComponentVisibleToInstantApp(component);
@@ -4300,16 +4325,16 @@
             // request for a specific component; if it hasn't been explicitly exposed through
             // property or instrumentation target, filter
             if (component != null) {
-                final PackageParser.Instrumentation instrumentation =
+                final ParsedInstrumentation instrumentation =
                         mInstrumentation.get(component);
                 if (instrumentation != null
-                        && isCallerSameApp(instrumentation.info.targetPackage, callingUid)) {
+                        && isCallerSameApp(instrumentation.getTargetPackage(), callingUid)) {
                     return false;
                 }
                 return !isComponentVisibleToInstantApp(component, componentType);
             }
             // request for application; if no components have been explicitly exposed, filter
-            return !ps.pkg.visibleToInstantApps;
+            return !ps.pkg.isVisibleToInstantApps();
         }
         if (ps.getInstantApp(userId)) {
             // caller can see all components of all instant applications, don't filter
@@ -4358,12 +4383,12 @@
         }
 
         // No package means no static lib as it is always on internal storage
-        if (ps == null || ps.pkg == null || !ps.pkg.applicationInfo.isStaticSharedLibrary()) {
+        if (ps == null || ps.pkg == null || !ps.pkg.isStaticSharedLibrary()) {
             return false;
         }
 
-        final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ps.pkg.staticSharedLibName,
-                ps.pkg.staticSharedLibVersion);
+        final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
+                ps.pkg.getStaticSharedLibName(), ps.pkg.getStaticSharedLibVersion());
         if (libraryInfo == null) {
             return false;
         }
@@ -4385,7 +4410,8 @@
                 if (index < 0) {
                     continue;
                 }
-                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libraryInfo.getLongVersion()) {
+                if (uidPs.pkg.getUsesStaticLibrariesVersions()[index]
+                        == libraryInfo.getLongVersion()) {
                     return false;
                 }
             }
@@ -4459,13 +4485,13 @@
 
         // reader
         synchronized (mLock) {
-            final PackageParser.Package p = mPackages.get(packageName);
+            final AndroidPackage p = mPackages.get(packageName);
             if (p != null && p.isMatch(flags)) {
-                PackageSetting ps = (PackageSetting) p.mExtras;
+                PackageSetting ps = getPackageSetting(p.getPackageName());
                 if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return -1;
                 }
-                return UserHandle.getUid(userId, p.applicationInfo.uid);
+                return UserHandle.getUid(userId, p.getUid());
             }
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -4489,9 +4515,9 @@
 
         // reader
         synchronized (mLock) {
-            final PackageParser.Package p = mPackages.get(packageName);
+            final AndroidPackage p = mPackages.get(packageName);
             if (p != null && p.isMatch(flags)) {
-                PackageSetting ps = (PackageSetting) p.mExtras;
+                PackageSetting ps = getPackageSetting(p.getPackageName());
                 if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return null;
                 }
@@ -4541,7 +4567,7 @@
                 }
                 return null;
             }
-            ApplicationInfo ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
+            ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
                     ps.readUserState(userId), userId);
             if (ai != null) {
                 ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
@@ -4579,7 +4605,7 @@
             packageName = resolveInternalPackageNameLPr(packageName,
                     PackageManager.VERSION_CODE_HIGHEST);
 
-            PackageParser.Package p = mPackages.get(packageName);
+            AndroidPackage p = mPackages.get(packageName);
             if (DEBUG_PACKAGE_INFO) Log.v(
                     TAG, "getApplicationInfo " + packageName
                     + ": " + p);
@@ -4593,7 +4619,7 @@
                     return null;
                 }
                 // Note: isEnabledLP() does not apply here - always return info
-                ApplicationInfo ai = PackageParser.generateApplicationInfo(
+                ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
                         p, flags, ps.readUserState(userId), userId);
                 if (ai != null) {
                     ai.packageName = resolveExternalPackageNameLPr(p);
@@ -4969,17 +4995,19 @@
         }
 
         synchronized (mLock) {
-            PackageParser.Activity a = mComponentResolver.getActivity(component);
+            ParsedActivity a = mComponentResolver.getActivity(component);
 
             if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
-            if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
+
+            AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
+            if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
                 if (shouldFilterApplicationLocked(
                         ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                     return null;
                 }
-                return PackageParser.generateActivityInfo(
+                return PackageInfoUtils.generateActivityInfo(pkg,
                         a, flags, ps.readUserState(userId), userId);
             }
             if (mResolveComponentName.equals(component)) {
@@ -5016,7 +5044,7 @@
             }
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
-            PackageParser.Activity a = mComponentResolver.getActivity(component);
+            ParsedActivity a = mComponentResolver.getActivity(component);
             if (a == null) {
                 return false;
             }
@@ -5046,17 +5074,27 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get receiver info");
         synchronized (mLock) {
-            PackageParser.Activity a = mComponentResolver.getReceiver(component);
+            ParsedActivity a = mComponentResolver.getReceiver(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getReceiverInfo " + component + ": " + a);
-            if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
+
+            if (a == null) {
+                return null;
+            }
+
+            AndroidPackage pkg = mPackages.get(a.getPackageName());
+            if (pkg == null) {
+                return null;
+            }
+
+            if (mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
                 if (shouldFilterApplicationLocked(
                         ps, callingUid, component, TYPE_RECEIVER, userId)) {
                     return null;
                 }
-                return PackageParser.generateActivityInfo(
+                return PackageInfoUtils.generateActivityInfo(pkg,
                         a, flags, ps.readUserState(userId), userId);
             }
         }
@@ -5235,13 +5273,13 @@
                 }
                 // If the dependent is a static shared lib, use the public package name
                 String dependentPackageName = ps.name;
-                if (ps.pkg != null && ps.pkg.applicationInfo.isStaticSharedLibrary()) {
-                    dependentPackageName = ps.pkg.manifestPackageName;
+                if (ps.pkg != null && ps.pkg.isStaticSharedLibrary()) {
+                    dependentPackageName = ps.pkg.getManifestPackageName();
                 }
                 versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
             } else if (ps.pkg != null) {
-                if (ArrayUtils.contains(ps.pkg.usesLibraries, libName)
-                        || ArrayUtils.contains(ps.pkg.usesOptionalLibraries, libName)) {
+                if (ArrayUtils.contains(ps.pkg.getUsesLibraries(), libName)
+                        || ArrayUtils.contains(ps.pkg.getUsesOptionalLibraries(), libName)) {
                     if (versionedPackages == null) {
                         versionedPackages = new ArrayList<>();
                     }
@@ -5261,17 +5299,22 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get service info");
         synchronized (mLock) {
-            PackageParser.Service s = mComponentResolver.getService(component);
+            ParsedService s = mComponentResolver.getService(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
-                TAG, "getServiceInfo " + component + ": " + s);
-            if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
+                    TAG, "getServiceInfo " + component + ": " + s);
+            if (s == null) {
+                return null;
+            }
+
+            AndroidPackage pkg = mPackages.get(s.getPackageName());
+            if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
                 if (shouldFilterApplicationLocked(
                         ps, callingUid, component, TYPE_SERVICE, userId)) {
                     return null;
                 }
-                return PackageParser.generateServiceInfo(
+                return PackageInfoUtils.generateServiceInfo(pkg,
                         s, flags, ps.readUserState(userId), userId);
             }
         }
@@ -5286,18 +5329,27 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get provider info");
         synchronized (mLock) {
-            PackageParser.Provider p = mComponentResolver.getProvider(component);
+            ParsedProvider p = mComponentResolver.getProvider(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
-                TAG, "getProviderInfo " + component + ": " + p);
-            if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
+                    TAG, "getProviderInfo " + component + ": " + p);
+            if (p == null) {
+                return null;
+            }
+
+            AndroidPackage pkg = mPackages.get(p.getPackageName());
+            if (pkg == null) {
+                return null;
+            }
+
+            if (mSettings.isEnabledAndMatchLPr(pkg, p, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
                 if (shouldFilterApplicationLocked(
                         ps, callingUid, component, TYPE_PROVIDER, userId)) {
                     return null;
                 }
-                return PackageParser.generateProviderInfo(
-                        p, flags, ps.readUserState(userId), userId);
+                PackageUserState state = ps.readUserState(userId);
+                return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId);
             }
         }
         return null;
@@ -5555,21 +5607,21 @@
     @Override
     public int checkSignatures(String pkg1, String pkg2) {
         synchronized (mLock) {
-            final PackageParser.Package p1 = mPackages.get(pkg1);
-            final PackageParser.Package p2 = mPackages.get(pkg2);
-            if (p1 == null || p1.mExtras == null
-                    || p2 == null || p2.mExtras == null) {
+            final AndroidPackage p1 = mPackages.get(pkg1);
+            final AndroidPackage p2 = mPackages.get(pkg2);
+            final PackageSetting ps1 = p1 == null ? null : getPackageSetting(p1.getPackageName());
+            final PackageSetting ps2 = p2 == null ? null : getPackageSetting(p2.getPackageName());
+            if (p1 == null || ps1 == null || p2 == null || ps2 == null) {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
-            final PackageSetting ps1 = (PackageSetting) p1.mExtras;
-            final PackageSetting ps2 = (PackageSetting) p2.mExtras;
             if (shouldFilterApplicationLocked(ps1, callingUid, callingUserId)
                     || shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
-            return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
+            return compareSignatures(p1.getSigningDetails().signatures,
+                    p2.getSigningDetails().signatures);
         }
     }
 
@@ -5632,21 +5684,21 @@
             String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
 
         synchronized (mLock) {
-            final PackageParser.Package p = mPackages.get(packageName);
-            if (p == null || p.mExtras == null) {
+            final AndroidPackage p = mPackages.get(packageName);
+            final PackageSetting ps = getPackageSetting(p.getPackageName());
+            if (p == null || ps == null) {
                 return false;
             }
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
-            final PackageSetting ps = (PackageSetting) p.mExtras;
             if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 return false;
             }
             switch (type) {
                 case CERT_INPUT_RAW_X509:
-                    return p.mSigningDetails.hasCertificate(certificate);
+                    return p.getSigningDetails().hasCertificate(certificate);
                 case CERT_INPUT_SHA256:
-                    return p.mSigningDetails.hasSha256Certificate(certificate);
+                    return p.getSigningDetails().hasSha256Certificate(certificate);
                 default:
                     return false;
             }
@@ -5699,16 +5751,16 @@
      * external storage) is less than the version where package signatures
      * were updated, return true.
      */
-    private boolean isCompatSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
-        return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+    private boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
+        return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
     }
 
     private static boolean isCompatSignatureUpdateNeeded(VersionInfo ver) {
         return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
     }
 
-    private boolean isRecoverSignatureUpdateNeeded(PackageParser.Package scannedPkg) {
-        return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(scannedPkg));
+    private boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
+        return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
     }
 
     private static boolean isRecoverSignatureUpdateNeeded(VersionInfo ver) {
@@ -5727,24 +5779,23 @@
             final List<String> result = new ArrayList<>();
             if (instantAppPkgName != null) {
                 // caller is an instant application; filter unexposed applications
-                for (PackageParser.Package pkg : mPackages.values()) {
-                    if (!pkg.visibleToInstantApps) {
+                for (AndroidPackage pkg : mPackages.values()) {
+                    if (!pkg.isVisibleToInstantApps()) {
                         continue;
                     }
-                    result.add(pkg.packageName);
+                    result.add(pkg.getPackageName());
                 }
             } else {
                 // caller is a normal application; filter instant applications
-                for (PackageParser.Package pkg : mPackages.values()) {
-                    final PackageSetting ps =
-                            pkg.mExtras != null ? (PackageSetting) pkg.mExtras : null;
+                for (AndroidPackage pkg : mPackages.values()) {
+                    final PackageSetting ps = getPackageSetting(pkg.getPackageName());
                     if (ps != null
                             && ps.getInstantApp(callingUserId)
                             && !mInstantAppRegistry.isInstantAccessGranted(
                                     callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
                         continue;
                     }
-                    result.add(pkg.packageName);
+                    result.add(pkg.getPackageName());
                 }
             }
             return result;
@@ -6590,7 +6641,7 @@
             if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
                 final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
-                return isInstantApp ? ps.pkg.packageName : null;
+                return isInstantApp ? ps.pkg.getPackageName() : null;
             }
         }
         return null;
@@ -6662,9 +6713,11 @@
                     list.add(ri);
                 }
             }
-            return applyPostResolutionFilter(
+
+            List<ResolveInfo> result = applyPostResolutionFilter(
                     list, instantAppPkgName, allowDynamicSplits, filterCallingUid, resolveForStart,
                     userId, intent);
+            return result;
         }
 
         // reader
@@ -6741,11 +6794,11 @@
                     sortResult = true;
                 }
             } else {
-                final PackageParser.Package pkg = mPackages.get(pkgName);
+                final AndroidPackage pkg = mPackages.get(pkgName);
                 result = null;
                 if (pkg != null) {
                     result = filterIfNotSystemUser(mComponentResolver.queryActivities(
-                            intent, resolvedType, flags, pkg.activities, userId), userId);
+                            intent, resolvedType, flags, pkg.getActivities(), userId), userId);
                 }
                 if (result == null || result.size() == 0) {
                     // the caller wants to resolve for a particular package; however, there
@@ -7638,10 +7691,10 @@
                         result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                         intent);
             }
-            final PackageParser.Package pkg = mPackages.get(pkgName);
+            final AndroidPackage pkg = mPackages.get(pkgName);
             if (pkg != null) {
                 final List<ResolveInfo> result = mComponentResolver.queryReceivers(
-                        intent, resolvedType, flags, pkg.receivers, userId);
+                        intent, resolvedType, flags, pkg.getReceivers(), userId);
                 return applyPostResolutionFilter(
                         result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
                         intent);
@@ -7740,11 +7793,11 @@
                         mComponentResolver.queryServices(intent, resolvedType, flags, userId),
                         instantAppPkgName);
             }
-            final PackageParser.Package pkg = mPackages.get(pkgName);
+            final AndroidPackage pkg = mPackages.get(pkgName);
             if (pkg != null) {
                 return applyPostServiceResolutionFilter(
-                        mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services,
-                                userId),
+                        mComponentResolver.queryServices(intent, resolvedType, flags,
+                                pkg.getServices(), userId),
                         instantAppPkgName);
             }
             return Collections.emptyList();
@@ -7858,11 +7911,11 @@
                         mComponentResolver.queryProviders(intent, resolvedType, flags, userId),
                         instantAppPkgName);
             }
-            final PackageParser.Package pkg = mPackages.get(pkgName);
+            final AndroidPackage pkg = mPackages.get(pkgName);
             if (pkg != null) {
                 return applyPostContentProviderResolutionFilter(
                         mComponentResolver.queryProviders(intent, resolvedType, flags,
-                                pkg.providers, userId),
+                                pkg.getProviders(), userId),
                         instantAppPkgName);
             }
             return Collections.emptyList();
@@ -7947,16 +8000,15 @@
                 }
             } else {
                 list = new ArrayList<>(mPackages.size());
-                for (PackageParser.Package p : mPackages.values()) {
-                    final PackageSetting ps = (PackageSetting) p.mExtras;
+                for (AndroidPackage p : mPackages.values()) {
+                    final PackageSetting ps = getPackageSetting(p.getPackageName());
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
                     if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                         continue;
                     }
-                    final PackageInfo pi = generatePackageInfo((PackageSetting)
-                            p.mExtras, flags, userId);
+                    final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                     if (pi != null) {
                         list.add(pi);
                     }
@@ -8035,8 +8087,8 @@
                             userId);
                 }
             } else {
-                for (PackageParser.Package pkg : mPackages.values()) {
-                    PackageSetting ps = (PackageSetting)pkg.mExtras;
+                for (AndroidPackage pkg : mPackages.values()) {
+                    PackageSetting ps = getPackageSetting(pkg.getPackageName());
                     if (ps != null) {
                         addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
                                 userId);
@@ -8089,7 +8141,7 @@
                         if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                             continue;
                         }
-                        ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
+                        ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, effectiveFlags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
                             ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
@@ -8106,16 +8158,16 @@
                 }
             } else {
                 list = new ArrayList<>(mPackages.size());
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mExtras != null) {
-                        PackageSetting ps = (PackageSetting) p.mExtras;
+                for (AndroidPackage p : mPackages.values()) {
+                    final PackageSetting ps = getPackageSetting(p.getPackageName());
+                    if (ps != null) {
                         if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                             continue;
                         }
                         if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                             continue;
                         }
-                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+                        ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
                             ai.packageName = resolveExternalPackageNameLPr(p);
@@ -8171,7 +8223,6 @@
                 callingUid = mIsolatedOwners.get(callingUid);
             }
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            PackageParser.Package pkg = mPackages.get(packageName);
             final boolean returnAllowed =
                     ps != null
                     && (isCallerSameApp(packageName, callingUid)
@@ -8242,9 +8293,9 @@
     }
 
     private boolean isCallerSameApp(String packageName, int uid) {
-        PackageParser.Package pkg = mPackages.get(packageName);
+        AndroidPackage pkg = mPackages.get(packageName);
         return pkg != null
-                && UserHandle.getAppId(uid) == pkg.applicationInfo.uid;
+                && UserHandle.getAppId(uid) == pkg.getUid();
     }
 
     @Override
@@ -8260,23 +8311,22 @@
 
         // reader
         synchronized (mLock) {
-            final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+            final Iterator<AndroidPackage> i = mPackages.values().iterator();
             final int userId = UserHandle.getCallingUserId();
             while (i.hasNext()) {
-                final PackageParser.Package p = i.next();
-                if (p.applicationInfo == null) continue;
+                final AndroidPackage p = i.next();
 
                 final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
-                        && !p.applicationInfo.isDirectBootAware();
+                        && !p.isDirectBootAware();
                 final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
-                        && p.applicationInfo.isDirectBootAware();
+                        && p.isDirectBootAware();
 
-                if ((p.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
+                if ((p.getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0
                         && (!mSafeMode || isSystemApp(p))
                         && (matchesUnaware || matchesAware)) {
-                    PackageSetting ps = mSettings.mPackages.get(p.packageName);
+                    PackageSetting ps = mSettings.mPackages.get(p.getPackageName());
                     if (ps != null) {
-                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+                        ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
                                 ps.readUserState(userId), userId);
                         if (ai != null) {
                             finalList.add(ai);
@@ -8298,7 +8348,8 @@
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, name);
         final int callingUid = Binder.getCallingUid();
-        final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
+        final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, callingUid,
+                userId);
         if (providerInfo == null) {
             return null;
         }
@@ -8380,8 +8431,8 @@
                     ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
                 return null;
             }
-            final PackageParser.Instrumentation i = mInstrumentation.get(component);
-            return PackageParser.generateInstrumentationInfo(i, flags);
+            final ParsedInstrumentation i = mInstrumentation.get(component);
+            return PackageInfoUtils.generateInstrumentationInfo(i, flags);
         }
     }
 
@@ -8403,12 +8454,12 @@
 
         // reader
         synchronized (mLock) {
-            final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
+            final Iterator<ParsedInstrumentation> i = mInstrumentation.values().iterator();
             while (i.hasNext()) {
-                final PackageParser.Instrumentation p = i.next();
+                final ParsedInstrumentation p = i.next();
                 if (targetPackage == null
-                        || targetPackage.equals(p.info.targetPackage)) {
-                    InstrumentationInfo ii = PackageParser.generateInstrumentationInfo(p,
+                        || targetPackage.equals(p.getTargetPackage())) {
+                    InstrumentationInfo ii = PackageInfoUtils.generateInstrumentationInfo(p,
                             flags);
                     if (ii != null) {
                         finalList.add(ii);
@@ -8465,18 +8516,18 @@
                 if (throwable == null) {
                     // TODO(toddke): move lower in the scan chain
                     // Static shared libraries have synthetic package names
-                    if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
-                        renameStaticSharedLibraryPackage(parseResult.pkg);
+                    if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+                        renameStaticSharedLibraryPackage(parseResult.parsedPackage);
                     }
                     try {
-                        scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,
+                        addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                                 currentTime, null);
                     } catch (PackageManagerException e) {
                         errorCode = e.error;
                         Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
                     }
-                } else if (throwable instanceof PackageParser.PackageParserException) {
-                    PackageParser.PackageParserException e = (PackageParser.PackageParserException)
+                } else if (throwable instanceof PackageParserException) {
+                    PackageParserException e = (PackageParserException)
                             throwable;
                     errorCode = e.error;
                     Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());
@@ -8500,15 +8551,16 @@
         logCriticalInfo(priority, msg);
     }
 
-    private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg,
+    private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
             boolean forceCollect, boolean skipVerify) throws PackageManagerException {
         // When upgrading from pre-N MR1, verify the package time stamp using the package
         // directory and not the APK file.
         final long lastModifiedTime = mIsPreNMR1Upgrade
-                ? new File(pkg.codePath).lastModified() : getLastModifiedTime(pkg);
-        final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(pkg);
+                ? new File(parsedPackage.getCodePath()).lastModified()
+                : getLastModifiedTime(parsedPackage);
+        final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
         if (ps != null && !forceCollect
-                && ps.codePathString.equals(pkg.codePath)
+                && ps.codePathString.equals(parsedPackage.getCodePath())
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
@@ -8518,21 +8570,21 @@
                             != SignatureSchemeVersion.UNKNOWN) {
                 // Optimization: reuse the existing cached signing data
                 // if the package appears to be unchanged.
-                pkg.mSigningDetails =
-                        new PackageParser.SigningDetails(ps.signatures.mSigningDetails);
+                parsedPackage.setSigningDetails(
+                        new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
                 return;
             }
 
             Slog.w(TAG, "PackageSetting for " + ps.name
                     + " is missing signatures.  Collecting certs again to recover them.");
         } else {
-            Slog.i(TAG, pkg.codePath + " changed; collecting certs" +
+            Slog.i(TAG, parsedPackage.getCodePath() + " changed; collecting certs" +
                     (forceCollect ? " (forced)" : ""));
         }
 
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
-            PackageParser.collectCertificates(pkg, skipVerify);
+            ApkParseUtils.collectCertificates(parsedPackage, skipVerify);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
         } finally {
@@ -8546,20 +8598,20 @@
      */
     private void maybeClearProfilesForUpgradesLI(
             @Nullable PackageSetting originalPkgSetting,
-            @NonNull PackageParser.Package currentPkg) {
+            @NonNull AndroidPackage pkg) {
         if (originalPkgSetting == null || !isDeviceUpgrading()) {
           return;
         }
-        if (originalPkgSetting.versionCode == currentPkg.mVersionCode) {
+        if (originalPkgSetting.versionCode == pkg.getVersionCode()) {
           return;
         }
 
-        clearAppProfilesLIF(currentPkg, UserHandle.USER_ALL);
+        clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
         if (DEBUG_INSTALL) {
             Slog.d(TAG, originalPkgSetting.name
                   + " clear profile due to version change "
                   + originalPkgSetting.versionCode + " != "
-                  + currentPkg.mVersionCode);
+                  + pkg.getVersionCode());
         }
     }
 
@@ -8568,7 +8620,7 @@
      *  @see #scanPackageLI(File, int, int, long, UserHandle)
      */
     @GuardedBy({"mInstallLock", "mLock"})
-    private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
+    private AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
         try {
@@ -8583,7 +8635,7 @@
      *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
      */
     @GuardedBy({"mInstallLock", "mLock"})
-    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
+    private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
         PackageParser pp = new PackageParser();
@@ -8593,9 +8645,9 @@
         pp.setCallback(mPackageParserCallback);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
-        final PackageParser.Package pkg;
+        final ParsedPackage parsedPackage;
         try {
-            pkg = pp.parsePackage(scanFile, parseFlags);
+            parsedPackage = pp.parseParsedPackage(scanFile, parseFlags, false);
         } catch (PackageParserException e) {
             throw PackageManagerException.from(e);
         } finally {
@@ -8603,66 +8655,25 @@
         }
 
         // Static shared libraries have synthetic package names
-        if (pkg.applicationInfo.isStaticSharedLibrary()) {
-            renameStaticSharedLibraryPackage(pkg);
+        if (parsedPackage.isStaticSharedLibrary()) {
+            renameStaticSharedLibraryPackage(parsedPackage);
         }
 
-        return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
-    }
-
-    /**
-     *  Scans a package and returns the newly parsed package.
-     *  @throws PackageManagerException on a parse error.
-     */
-    @GuardedBy({"mInstallLock", "mLock"})
-    private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
-            final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user)
-                    throws PackageManagerException {
-        // If the package has children and this is the first dive in the function
-        // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
-        // packages (parent and children) would be successfully scanned before the
-        // actual scan since scanning mutates internal state and we want to atomically
-        // install the package and its children.
-        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
-            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
-                scanFlags |= SCAN_CHECK_ONLY;
-            }
-        } else {
-            scanFlags &= ~SCAN_CHECK_ONLY;
-        }
-
-        // Scan the parent
-        PackageParser.Package scannedPkg = addForInitLI(pkg, parseFlags,
-                scanFlags, currentTime, user);
-
-        // Scan the children
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPackage = pkg.childPackages.get(i);
-            addForInitLI(childPackage, parseFlags, scanFlags,
-                    currentTime, user);
-        }
-
-
-        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            return scanPackageChildLI(pkg, parseFlags, scanFlags, currentTime, user);
-        }
-
-        return scannedPkg;
+        return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
     }
 
     /**
      * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
-    private boolean canSkipForcedPackageVerification(PackageParser.Package pkg) {
-        if (!canSkipForcedApkVerification(pkg.baseCodePath)) {
+    private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+        if (!canSkipForcedApkVerification(pkg.getBaseCodePath())) {
             return false;
         }
         // TODO: Allow base and splits to be verified individually.
-        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-            for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                if (!canSkipForcedApkVerification(pkg.splitCodePaths[i])) {
+        String[] splitCodePaths = pkg.getSplitCodePaths();
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            for (int i = 0; i < splitCodePaths.length; i++) {
+                if (!canSkipForcedApkVerification(splitCodePaths[i])) {
                     return false;
                 }
             }
@@ -8711,7 +8722,7 @@
      * <p>NOTE: The return value should be removed. It's the passed in package object.
      */
     @GuardedBy({"mInstallLock", "mLock"})
-    private PackageParser.Package addForInitLI(PackageParser.Package pkg,
+    private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
             @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
                     throws PackageManagerException {
@@ -8727,25 +8738,27 @@
         // stack [such as scanPackageOnly()]. However, we verify the application
         // info prior to that [in scanPackageNew()] and thus have to setup
         // the application info early.
-        pkg.setApplicationVolumeUuid(pkg.volumeUuid);
-        pkg.setApplicationInfoCodePath(pkg.codePath);
-        pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
-        pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
-        pkg.setApplicationInfoResourcePath(pkg.codePath);
-        pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
-        pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
+        // TODO(b/135203078): Remove all of these application info calls
+        parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
+                .setApplicationInfoCodePath(parsedPackage.getCodePath())
+                .setApplicationInfoResourcePath(parsedPackage.getCodePath())
+                .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
+                .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
 
         synchronized (mLock) {
-            renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
-            final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+            renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
+            final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
             if (realPkgName != null) {
-                ensurePackageRenamed(pkg, renamedPkgName);
+                ensurePackageRenamed(parsedPackage, renamedPkgName);
             }
-            final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
-            final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName);
+            final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
+                    renamedPkgName);
+            final PackageSetting installedPkgSetting = mSettings.getPackageLPr(
+                    parsedPackage.getPackageName());
             pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
             pkgAlreadyExists = pkgSetting != null;
-            final String disabledPkgName = pkgAlreadyExists ? pkgSetting.name : pkg.packageName;
+            final String disabledPkgName =
+                    pkgAlreadyExists ? pkgSetting.name : parsedPackage.getPackageName();
             disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(disabledPkgName);
             isSystemPkgUpdated = disabledPkgSetting != null;
 
@@ -8753,49 +8766,29 @@
                 Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
             }
 
-            final SharedUserSetting sharedUserSetting = (pkg.mSharedUserId != null)
-                    ? mSettings.getSharedUserLPw(pkg.mSharedUserId,
+            final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+                    ? mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
                             0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
                     : null;
             if (DEBUG_PACKAGE_SCANNING
                     && (parseFlags & PackageParser.PARSE_CHATTY) != 0
                     && sharedUserSetting != null) {
-                Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+                Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
                         + " (uid=" + sharedUserSetting.userId + "):"
                         + " packages=" + sharedUserSetting.packages);
             }
 
             if (scanSystemPartition) {
-                // Potentially prune child packages. If the application on the /system
-                // partition has been updated via OTA, but, is still disabled by a
-                // version on /data, cycle through all of its children packages and
-                // remove children that are no longer defined.
                 if (isSystemPkgUpdated) {
-                    final int scannedChildCount = (pkg.childPackages != null)
-                            ? pkg.childPackages.size() : 0;
-                    final int disabledChildCount = disabledPkgSetting.childPackageNames != null
-                            ? disabledPkgSetting.childPackageNames.size() : 0;
-                    for (int i = 0; i < disabledChildCount; i++) {
-                        String disabledChildPackageName =
-                                disabledPkgSetting.childPackageNames.get(i);
-                        boolean disabledPackageAvailable = false;
-                        for (int j = 0; j < scannedChildCount; j++) {
-                            PackageParser.Package childPkg = pkg.childPackages.get(j);
-                            if (childPkg.packageName.equals(disabledChildPackageName)) {
-                                disabledPackageAvailable = true;
-                                break;
-                            }
-                        }
-                        if (!disabledPackageAvailable) {
-                            mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
-                        }
-                    }
                     // we're updating the disabled package, so, scan it as the package setting
-                    final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, null,
-                            disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
-                            null /* originalPkgSetting */, null, parseFlags, scanFlags,
-                            (pkg == mPlatformPackage), user);
-                    applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
+                    boolean isPlatformPackage = mPlatformPackage != null
+                            && Objects.equals(mPlatformPackage.getPackageName(),
+                            parsedPackage.getPackageName());
+                    final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
+                            null, disabledPkgSetting /* pkgSetting */,
+                            null /* disabledPkgSetting */, null /* originalPkgSetting */,
+                            null, parseFlags, scanFlags, isPlatformPackage, user);
+                    applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage);
                     final ScanResult scanResult =
                             scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
                     if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
@@ -8806,9 +8799,9 @@
         }
 
         final boolean newPkgChangedPaths =
-                pkgAlreadyExists && !pkgSetting.codePathString.equals(pkg.codePath);
+                pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
         final boolean newPkgVersionGreater =
-                pkgAlreadyExists && pkg.getLongVersionCode() > pkgSetting.versionCode;
+                pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
         final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
                 && newPkgChangedPaths && newPkgVersionGreater;
         if (isSystemPkgBetter) {
@@ -8824,12 +8817,13 @@
             logCriticalInfo(Log.WARN,
                     "System package updated;"
                     + " name: " + pkgSetting.name
-                    + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
-                    + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+                    + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
+                    + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
 
             final InstallArgs args = createInstallArgsForExisting(
                     pkgSetting.codePathString,
-                    pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
+                    pkgSetting.resourcePathString, getAppDexInstructionSets(
+                            pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
             args.cleanUpResourcesLI();
             synchronized (mLock) {
                 mSettings.enableSystemPackageLPw(pkgSetting.name);
@@ -8840,9 +8834,10 @@
             // The version of the application on the /system partition is less than or
             // 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 " + pkgSetting.versionCode
-                    + " better than this " + pkg.getLongVersionCode());
+            throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+                    + " at " + parsedPackage.getCodePath() + " ignored: updated version "
+                    + pkgSetting.versionCode + " better than this "
+                    + parsedPackage.getLongVersionCode());
         }
 
         // Verify certificates against what was last scanned. Force re-collecting certificate in two
@@ -8853,18 +8848,18 @@
         final boolean forceCollect = scanSystemPartition ? mIsUpgrade
                 : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
         if (DEBUG_VERIFY && forceCollect) {
-            Slog.d(TAG, "Force collect certificate of " + pkg.packageName);
+            Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
         }
 
         // Full APK verification can be skipped during certificate collection, only if the file is
         // in verified partition, or can be verified on access (when apk verity is enabled). In both
         // cases, only data in Signing Block is verified instead of the whole file.
         final boolean skipVerify = scanSystemPartition
-                || (forceCollect && canSkipForcedPackageVerification(pkg));
-        collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
+                || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+        collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
 
         // Reset profile if the application version is changed
-        maybeClearProfilesForUpgradesLI(pkgSetting, pkg);
+        maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
 
         /*
          * A new system app appeared, but we already had a non-system one of the
@@ -8876,17 +8871,20 @@
         if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
                 && !pkgSetting.isSystem()) {
 
-            if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
+            if (!parsedPackage.getSigningDetails()
+                    .checkCapability(pkgSetting.signatures.mSigningDetails,
                     PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
                             && !pkgSetting.signatures.mSigningDetails.checkCapability(
-                                    pkg.mSigningDetails,
+                                    parsedPackage.getSigningDetails(),
                                     PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
                 logCriticalInfo(Log.WARN,
                         "System package signature mismatch;"
                         + " name: " + pkgSetting.name);
-                try (PackageFreezer freezer = freezePackage(pkg.packageName,
+                try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
+                        parsedPackage.getPackageName(),
                         "scanPackageInternalLI")) {
-                    deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
+                    deletePackageLIF(parsedPackage.getPackageName(), null, true, null, 0, null,
+                            false, null);
                 }
                 pkgSetting = null;
             } else if (newPkgVersionGreater) {
@@ -8895,12 +8893,15 @@
                 // and replace it with the version on /system.
                 logCriticalInfo(Log.WARN,
                         "System package enabled;"
-                        + " name: " + pkgSetting.name
-                        + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
-                        + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
+                                + " name: " + pkgSetting.name
+                                + "; " + pkgSetting.versionCode + " --> "
+                                + parsedPackage.getLongVersionCode()
+                                + "; " + pkgSetting.codePathString + " --> "
+                                + parsedPackage.getCodePath());
                 InstallArgs args = createInstallArgsForExisting(
                         pkgSetting.codePathString,
-                        pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
+                        pkgSetting.resourcePathString, getAppDexInstructionSets(
+                                pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
                 }
@@ -8911,13 +8912,15 @@
                 shouldHideSystemApp = true;
                 logCriticalInfo(Log.INFO,
                         "System package disabled;"
-                        + " name: " + pkgSetting.name
-                        + "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
-                        + "; new: " + pkg.codePath + " @ " + pkg.codePath);
+                                + " name: " + pkgSetting.name
+                                + "; old: " + pkgSetting.codePathString + " @ "
+                                + pkgSetting.versionCode
+                                + "; new: " + parsedPackage.getCodePath() + " @ "
+                                + parsedPackage.getCodePath());
             }
         }
 
-        final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
+        final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
         if (scanResult.success) {
             synchronized (mLock) {
@@ -8930,7 +8933,7 @@
                                     mSharedLibraries,
                                     mPackages,
                                     Collections.singletonMap(
-                                            pkgName, getSettingsVersionForPackage(pkg)),
+                                            pkgName, getSettingsVersionForPackage(parsedPackage)),
                                     Collections.singletonMap(pkgName,
                                             getSharedLibLatestVersionSetting(scanResult))),
                             mSettings.mKeySetManagerService);
@@ -8947,16 +8950,17 @@
 
         if (shouldHideSystemApp) {
             synchronized (mLock) {
-                mSettings.disableSystemPackageLPw(pkg.packageName, true);
+                mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
             }
         }
         return scanResult.pkgSetting.pkg;
     }
 
-    private static void renameStaticSharedLibraryPackage(PackageParser.Package pkg) {
+    // TODO:(b/135203078): Move to parsing
+    private static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
         // Derive the new package synthetic package name
-        pkg.setPackageName(pkg.packageName + STATIC_SHARED_LIB_DELIMITER
-                + pkg.staticSharedLibVersion);
+        parsedPackage.setPackageName(parsedPackage.getPackageName() + STATIC_SHARED_LIB_DELIMITER
+                + parsedPackage.getStaticSharedLibVersion());
     }
 
     static String fixProcessName(String defProcessName, String processName) {
@@ -9057,7 +9061,7 @@
             return;
         }
 
-        List<PackageParser.Package> pkgs;
+        List<AndroidPackage> pkgs;
         synchronized (mLock) {
             pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
         }
@@ -9080,8 +9084,8 @@
     /*
      * Return the prebuilt profile path given a package base code path.
      */
-    private static String getPrebuildProfilePath(PackageParser.Package pkg) {
-        return pkg.baseCodePath + ".prof";
+    private static String getPrebuildProfilePath(AndroidPackage pkg) {
+        return pkg.getBaseCodePath() + ".prof";
     }
 
     /**
@@ -9090,7 +9094,7 @@
      * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
      * and {@code numberOfPackagesFailed}.
      */
-    private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
+    private int[] performDexOptUpgrade(List<AndroidPackage> pkgs, boolean showDialog,
             final int compilationReason, boolean bootComplete) {
 
         int numberOfPackagesVisited = 0;
@@ -9099,7 +9103,7 @@
         int numberOfPackagesFailed = 0;
         final int numberOfPackagesToDexopt = pkgs.size();
 
-        for (PackageParser.Package pkg : pkgs) {
+        for (AndroidPackage pkg : pkgs) {
             numberOfPackagesVisited++;
 
             boolean useProfileForDexopt = false;
@@ -9115,7 +9119,7 @@
                         // PackageDexOptimizer to prevent this happening on first boot. The issue
                         // is that we don't have a good way to say "do this only once".
                         if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                pkg.applicationInfo.uid, pkg.packageName,
+                                pkg.getUid(), pkg.getPackageName(),
                                 ArtManager.getProfileName(null))) {
                             Log.e(TAG, "Installer failed to copy system profile!");
                         } else {
@@ -9128,11 +9132,12 @@
                                 e);
                     }
                 } else {
-                    PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+                    PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(
+                            pkg.getPackageName());
                     // Handle compressed APKs in this path. Only do this for stubs with profiles to
                     // minimize the number off apps being speed-profile compiled during first boot.
                     // The other paths will not change the filter.
-                    if (disabledPs != null && disabledPs.pkg.isStub) {
+                    if (disabledPs != null && disabledPs.pkg.isStub()) {
                         // The package is the stub one, remove the stub suffix to get the normal
                         // package and APK names.
                         String systemProfilePath =
@@ -9151,7 +9156,7 @@
                                 // issue is that we don't have a good way to say "do this only
                                 // once".
                                 if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(),
-                                        pkg.applicationInfo.uid, pkg.packageName,
+                                        pkg.getUid(), pkg.getPackageName(),
                                         ArtManager.getProfileName(null))) {
                                     Log.e(TAG, "Failed to copy system profile for stub package!");
                                 } else {
@@ -9168,7 +9173,7 @@
 
             if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
                 if (DEBUG_DEXOPT) {
-                    Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
+                    Log.i(TAG, "Skipping update of non-optimizable app " + pkg.getPackageName());
                 }
                 numberOfPackagesSkipped++;
                 continue;
@@ -9176,7 +9181,7 @@
 
             if (DEBUG_DEXOPT) {
                 Log.i(TAG, "Updating app " + numberOfPackagesVisited + " of " +
-                        numberOfPackagesToDexopt + ": " + pkg.packageName);
+                        numberOfPackagesToDexopt + ": " + pkg.getPackageName());
             }
 
             if (showDialog) {
@@ -9213,7 +9218,7 @@
                 dexoptFlags |= DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE;
             }
             int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
-                    pkg.packageName,
+                    pkg.getPackageName(),
                     pkgCompilationReason,
                     dexoptFlags));
 
@@ -9257,11 +9262,11 @@
 
     @GuardedBy("mLock")
     private void notifyPackageUseLocked(String packageName, int reason) {
-        final PackageParser.Package p = mPackages.get(packageName);
+        final AndroidPackage p = mPackages.get(packageName);
         if (p == null) {
             return;
         }
-        p.mLastPackageUsageTimeInMills[reason] = System.currentTimeMillis();
+        p.mutate().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
     }
 
     @Override
@@ -9341,7 +9346,7 @@
     */
     @Override
     public boolean compileLayouts(String packageName) {
-        PackageParser.Package pkg;
+        AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
@@ -9388,7 +9393,7 @@
     // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
     // if the package can now be considered up to date for the given filter.
     private int performDexOptInternal(DexoptOptions options) {
-        PackageParser.Package p;
+        AndroidPackage p;
         synchronized (mLock) {
             p = mPackages.get(options.getPackageName());
             if (p == null) {
@@ -9411,16 +9416,16 @@
     public ArraySet<String> getOptimizablePackages() {
         ArraySet<String> pkgs = new ArraySet<>();
         synchronized (mLock) {
-            for (PackageParser.Package p : mPackages.values()) {
+            for (AndroidPackage p : mPackages.values()) {
                 if (PackageDexOptimizer.canOptimizePackage(p)) {
-                    pkgs.add(p.packageName);
+                    pkgs.add(p.getPackageName());
                 }
             }
         }
         return pkgs;
     }
 
-    private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
+    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
             DexoptOptions options) {
         // Select the dex optimizer based on the force parameter.
         // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
@@ -9437,14 +9442,15 @@
         // and the first package that uses the library will dexopt it. The
         // others will see that the compiled code for the library is up to date.
         Collection<SharedLibraryInfo> deps = findSharedLibraries(p);
-        final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
+        final String[] instructionSets = getAppDexInstructionSets(p.getPrimaryCpuAbi(),
+                p.getSecondaryCpuAbi());
         if (!deps.isEmpty()) {
             DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
                     options.getCompilationReason(), options.getCompilerFilter(),
                     options.getSplitName(),
                     options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
             for (SharedLibraryInfo info : deps) {
-                PackageParser.Package depPackage = null;
+                AndroidPackage depPackage = null;
                 synchronized (mLock) {
                     depPackage = mPackages.get(info.getPackageName());
                 }
@@ -9452,7 +9458,7 @@
                     // TODO: Analyze and investigate if we (should) profile libraries.
                     pdo.performDexOpt(depPackage, instructionSets,
                             getOrCreateCompilerPackageStats(depPackage),
-                            mDexManager.getPackageUseInfoOrDefault(depPackage.packageName),
+                            mDexManager.getPackageUseInfoOrDefault(depPackage.getPackageName()),
                             libraryOptions);
                 } else {
                     // TODO(ngeoffray): Support dexopting system shared libraries.
@@ -9461,7 +9467,7 @@
         }
         return pdo.performDexOpt(p, instructionSets,
                 getOrCreateCompilerPackageStats(p),
-                mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
+                mDexManager.getPackageUseInfoOrDefault(p.getPackageName()), options);
     }
 
     /**
@@ -9502,11 +9508,11 @@
         }
     }
 
-    private static List<SharedLibraryInfo> findSharedLibraries(PackageParser.Package p) {
-        if (p.usesLibraryInfos != null) {
+    private static List<SharedLibraryInfo> findSharedLibraries(AndroidPackage p) {
+        if (p.getUsesLibraryInfos() != null) {
             ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
             Set<String> collectedNames = new HashSet<>();
-            for (SharedLibraryInfo info : p.usesLibraryInfos) {
+            for (SharedLibraryInfo info : p.getUsesLibraryInfos()) {
                 findSharedLibrariesRecursive(info, retValue, collectedNames);
             }
             return retValue;
@@ -9529,13 +9535,13 @@
         }
     }
 
-    List<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package pkg) {
+    List<AndroidPackage> findSharedNonSystemLibraries(AndroidPackage pkg) {
         List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
         if (!deps.isEmpty()) {
-            ArrayList<PackageParser.Package> retValue = new ArrayList<>();
+            ArrayList<AndroidPackage> retValue = new ArrayList<>();
             synchronized (mLock) {
                 for (SharedLibraryInfo info : deps) {
-                    PackageParser.Package depPackage = mPackages.get(info.getPackageName());
+                    AndroidPackage depPackage = mPackages.get(info.getPackageName());
                     if (depPackage != null) {
                         retValue.add(depPackage);
                     }
@@ -9573,9 +9579,9 @@
         return versionedLib.get(version);
     }
 
-    private SharedLibraryInfo getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
+    private SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
-                pkg.staticSharedLibName);
+                pkg.getStaticSharedLibName());
         if (versionedLib == null) {
             return null;
         }
@@ -9583,7 +9589,7 @@
         final int versionCount = versionedLib.size();
         for (int i = 0; i < versionCount; i++) {
             final long libVersion = versionedLib.keyAt(i);
-            if (libVersion < pkg.staticSharedLibVersion) {
+            if (libVersion < pkg.getStaticSharedLibVersion()) {
                 previousLibVersion = Math.max(previousLibVersion, libVersion);
             }
         }
@@ -9599,7 +9605,7 @@
         PackageSetting sharedLibPackage = null;
         synchronized (mLock) {
             final SharedLibraryInfo latestSharedLibraVersionLPr =
-                    getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg);
+                    getLatestSharedLibraVersionLPr(scanResult.request.parsedPackage);
             if (latestSharedLibraVersionLPr != null) {
                 sharedLibPackage = mSettings.getPackageLPr(
                         latestSharedLibraVersionLPr.getPackageName());
@@ -9628,7 +9634,7 @@
 
     @Override
     public void dumpProfiles(String packageName) {
-        PackageParser.Package pkg;
+        AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
@@ -9639,7 +9645,7 @@
         int callingUid = Binder.getCallingUid();
         if (callingUid != Process.SHELL_UID &&
             callingUid != Process.ROOT_UID &&
-            callingUid != pkg.applicationInfo.uid) {
+            callingUid != pkg.getUid()) {
             throw new SecurityException("dumpProfiles");
         }
 
@@ -9654,7 +9660,7 @@
     public void forceDexOpt(String packageName) {
         enforceSystemOrRoot("forceDexOpt");
 
-        PackageParser.Package pkg;
+        AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
@@ -9681,15 +9687,15 @@
     }
 
     @GuardedBy("mLock")
-    private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
+    private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
         if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             Slog.w(TAG, "Unable to update from " + oldPkg.name
-                    + " to " + newPkg.packageName
+                    + " to " + newPkg.getPackageName()
                     + ": old package not in system partition");
             return false;
         } else if (mPackages.get(oldPkg.name) != null) {
             Slog.w(TAG, "Unable to update from " + oldPkg.name
-                    + " to " + newPkg.packageName
+                    + " to " + newPkg.getPackageName()
                     + ": old package still exists");
             return false;
         }
@@ -9713,122 +9719,86 @@
         return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
     }
 
-    private void clearAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         clearAppDataLeafLIF(pkg, userId, flags);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            clearAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
-        }
 
         if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
             clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
         }
     }
 
-    private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void clearAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
         final PackageSetting ps;
         synchronized (mLock) {
-            ps = mSettings.mPackages.get(pkg.packageName);
+            ps = mSettings.mPackages.get(pkg.getPackageName());
         }
         for (int realUserId : resolveUserIds(userId)) {
             final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
             try {
-                mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
-                        ceDataInode);
+                mInstaller.clearAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId,
+                        flags, ceDataInode);
             } catch (InstallerException e) {
                 Slog.w(TAG, String.valueOf(e));
             }
         }
     }
 
-    private void destroyAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         destroyAppDataLeafLIF(pkg, userId, flags);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            destroyAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
-        }
     }
 
-    private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void destroyAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
         final PackageSetting ps;
         synchronized (mLock) {
-            ps = mSettings.mPackages.get(pkg.packageName);
+            ps = mSettings.mPackages.get(pkg.getPackageName());
         }
         for (int realUserId : resolveUserIds(userId)) {
             final long ceDataInode = (ps != null) ? ps.getCeDataInode(realUserId) : 0;
             try {
-                mInstaller.destroyAppData(pkg.volumeUuid, pkg.packageName, realUserId, flags,
-                        ceDataInode);
+                mInstaller.destroyAppData(pkg.getVolumeUuid(), pkg.getPackageName(), realUserId,
+                        flags, ceDataInode);
             } catch (InstallerException e) {
                 Slog.w(TAG, String.valueOf(e));
             }
-            mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
+            mDexManager.notifyPackageDataDestroyed(pkg.getPackageName(), userId);
         }
     }
 
-    private void destroyAppProfilesLIF(PackageParser.Package pkg) {
+    private void destroyAppProfilesLIF(AndroidPackage pkg) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         destroyAppProfilesLeafLIF(pkg);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
-        }
     }
 
-    private void destroyAppProfilesLeafLIF(PackageParser.Package pkg) {
+    private void destroyAppProfilesLeafLIF(AndroidPackage pkg) {
         try {
-            mInstaller.destroyAppProfiles(pkg.packageName);
+            mInstaller.destroyAppProfiles(pkg.getPackageName());
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
         }
     }
 
-    private void clearAppProfilesLIF(PackageParser.Package pkg, int userId) {
+    private void clearAppProfilesLIF(AndroidPackage pkg, int userId) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         mArtManagerService.clearAppProfiles(pkg);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            mArtManagerService.clearAppProfiles(pkg.childPackages.get(i));
-        }
-    }
-
-    private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime,
-            long lastUpdateTime) {
-        // Set parent install/update time
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
-        if (ps != null) {
-            ps.firstInstallTime = firstInstallTime;
-            ps.lastUpdateTime = lastUpdateTime;
-        }
-        // Set children install/update time
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = pkg.childPackages.get(i);
-            ps = (PackageSetting) childPkg.mExtras;
-            if (ps != null) {
-                ps.firstInstallTime = firstInstallTime;
-                ps.lastUpdateTime = lastUpdateTime;
-            }
-        }
     }
 
     @GuardedBy("mLock")
     private void applyDefiningSharedLibraryUpdateLocked(
-            PackageParser.Package pkg, SharedLibraryInfo libInfo,
+            AndroidPackage pkg, SharedLibraryInfo libInfo,
             BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
         // Note that libraries defined by this package may be null if:
         // - Package manager was unable to create the shared library. The package still
@@ -9837,14 +9807,14 @@
         // - Package manager is in a state where package isn't scanned yet. This will
         //   get called again after scanning to fix the dependencies.
         if (pkg.isLibrary()) {
-            if (pkg.staticSharedLibName != null) {
+            if (pkg.getStaticSharedLibName() != null) {
                 SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
-                        pkg.staticSharedLibName, pkg.staticSharedLibVersion);
+                        pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
                 if (definedLibrary != null) {
                     action.accept(definedLibrary, libInfo);
                 }
             } else {
-                for (String libraryName : pkg.libraryNames) {
+                for (String libraryName : pkg.getLibraryNames()) {
                     SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
                             libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
                     if (definedLibrary != null) {
@@ -9856,19 +9826,19 @@
     }
 
     @GuardedBy("mLock")
-    private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
-            SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
+    private void addSharedLibraryLPr(AndroidPackage pkg, Set<String> usesLibraryFiles,
+            SharedLibraryInfo libInfo, AndroidPackage changingLib) {
         if (libInfo.getPath() != null) {
             usesLibraryFiles.add(libInfo.getPath());
             return;
         }
-        PackageParser.Package p = mPackages.get(libInfo.getPackageName());
-        if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) {
+        AndroidPackage p = mPackages.get(libInfo.getPackageName());
+        if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) {
             // If we are doing this while in the middle of updating a library apk,
             // then we need to make sure to use that new apk for determining the
             // dependencies here.  (We haven't yet finished committing the new apk
             // to the package manager state.)
-            if (p == null || p.packageName.equals(changingLib.packageName)) {
+            if (p == null || p.getPackageName().equals(changingLib.getPackageName())) {
                 p = changingLib;
             }
         }
@@ -9878,23 +9848,23 @@
             applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> {
                 definingLibrary.addDependency(dependency);
             });
-            if (p.usesLibraryFiles != null) {
-                Collections.addAll(usesLibraryFiles, p.usesLibraryFiles);
+            if (p.getUsesLibraryFiles() != null) {
+                Collections.addAll(usesLibraryFiles, p.getUsesLibraryFiles());
             }
         }
     }
 
     @GuardedBy("mLock")
-    private void updateSharedLibrariesLocked(PackageParser.Package pkg,
-            PackageParser.Package changingLib, Map<String, PackageParser.Package> availablePackages)
+    private void updateSharedLibrariesLocked(AndroidPackage pkg,
+            AndroidPackage changingLib, Map<String, AndroidPackage> availablePackages)
                     throws PackageManagerException {
         final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
                 collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null);
         executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
     }
 
-    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(PackageParser.Package pkg,
-            Map<String, PackageParser.Package> availablePackages,
+    private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
+            Map<String, AndroidPackage> availablePackages,
             @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
             @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
             throws PackageManagerException {
@@ -9905,44 +9875,45 @@
         // that libraries are searched in the correct order) and must have no
         // duplicates.
         ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
-        if (pkg.usesLibraries != null) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesLibraries, null, null,
-                    pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, null,
+        if (pkg.getUsesLibraries() != null) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
+                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
                     availablePackages, existingLibraries, newLibraries);
         }
-        if (pkg.usesStaticLibraries != null) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesStaticLibraries,
-                    pkg.usesStaticLibrariesVersions, pkg.usesStaticLibrariesCertDigests,
-                    pkg.packageName, true, pkg.applicationInfo.targetSdkVersion, usesLibraryInfos,
+        if (pkg.getUsesStaticLibraries() != null) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
+                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
+                    pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
                     availablePackages, existingLibraries, newLibraries);
         }
-        if (pkg.usesOptionalLibraries != null) {
-            usesLibraryInfos = collectSharedLibraryInfos(pkg.usesOptionalLibraries,
-                    null, null, pkg.packageName, false, pkg.applicationInfo.targetSdkVersion,
+        if (pkg.getUsesOptionalLibraries() != null) {
+            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
+                    null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
                     usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
         }
         return usesLibraryInfos;
     }
 
-    private void executeSharedLibrariesUpdateLPr(PackageParser.Package pkg,
-            PackageParser.Package changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
+    private void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
+            AndroidPackage changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
         // If the package provides libraries, clear their old dependencies.
         // This method will set them up again.
         applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
             definingLibrary.clearDependencies();
         });
         if (usesLibraryInfos != null) {
-            pkg.usesLibraryInfos = usesLibraryInfos;
+            pkg.mutate().setUsesLibraryInfos(usesLibraryInfos);
             // Use LinkedHashSet to preserve the order of files added to
             // usesLibraryFiles while eliminating duplicates.
             Set<String> usesLibraryFiles = new LinkedHashSet<>();
             for (SharedLibraryInfo libInfo : usesLibraryInfos) {
                 addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib);
             }
-            pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
+            pkg.mutate().setUsesLibraryFiles(usesLibraryFiles.toArray(
+                    new String[usesLibraryFiles.size()]));
         } else {
-            pkg.usesLibraryInfos = null;
-            pkg.usesLibraryFiles = null;
+            pkg.mutate().setUsesLibraryInfos(null)
+                    .setUsesLibraryFiles(null);
         }
     }
 
@@ -9952,7 +9923,7 @@
             @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, boolean required, int targetSdk,
             @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
-            @NonNull final Map<String, PackageParser.Package> availablePackages,
+            @NonNull final Map<String, AndroidPackage> availablePackages,
             @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries,
             @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries)
             throws PackageManagerException {
@@ -9981,8 +9952,8 @@
                                     + " library " + libName + " version "
                                     + libraryInfo.getLongVersion() + "; failing!");
                     }
-                    PackageParser.Package libPkg =
-                            availablePackages.get(libraryInfo.getPackageName());
+                    AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName());
+                    SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
                     if (libPkg == null) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                 "Package " + packageName + " requires unavailable static shared"
@@ -9993,9 +9964,9 @@
                         // For apps targeting O MR1 we require explicit enumeration of all certs.
                         final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
                                 ? PackageUtils.computeSignaturesSha256Digests(
-                                libPkg.mSigningDetails.signatures)
+                                libPkg.signatures)
                                 : PackageUtils.computeSignaturesSha256Digests(
-                                        new Signature[]{libPkg.mSigningDetails.signatures[0]});
+                                        new Signature[]{libPkg.signatures[0]});
 
                         // Take a shortcut if sizes don't match. Note that if an app doesn't
                         // target O we don't parse the "additional-certificate" tags similarly
@@ -10025,7 +9996,7 @@
                         // if the new one has been blessed by the old
                         byte[] digestBytes = HexEncoding.decode(
                                 expectedCertDigests[0], false /* allowSingleChar */);
-                        if (!libPkg.mSigningDetails.hasSha256Certificate(digestBytes)) {
+                        if (!libPkg.hasSha256Certificate(digestBytes)) {
                             throw new PackageManagerException(
                                     INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                     "Package " + packageName + " requires differently signed" +
@@ -10057,28 +10028,28 @@
     }
 
     @GuardedBy("mLock")
-    private ArrayList<PackageParser.Package> updateAllSharedLibrariesLocked(
-            PackageParser.Package updatedPkg,
-            Map<String, PackageParser.Package> availablePackages) {
-        ArrayList<PackageParser.Package> resultList = null;
+    private ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
+            AndroidPackage updatedPkg,
+            Map<String, AndroidPackage> availablePackages) {
+        ArrayList<AndroidPackage> resultList = null;
         // Set of all descendants of a library; used to eliminate cycles
         ArraySet<String> descendants = null;
         // The current list of packages that need updating
-        ArrayList<PackageParser.Package> needsUpdating = null;
+        ArrayList<AndroidPackage> needsUpdating = null;
         if (updatedPkg != null) {
             needsUpdating = new ArrayList<>(1);
             needsUpdating.add(updatedPkg);
         }
         do {
-            final PackageParser.Package changingPkg =
+            final AndroidPackage changingPkg =
                     (needsUpdating == null) ? null : needsUpdating.remove(0);
             for (int i = mPackages.size() - 1; i >= 0; --i) {
-                final PackageParser.Package pkg = mPackages.valueAt(i);
+                final AndroidPackage pkg = mPackages.valueAt(i);
                 if (changingPkg != null
-                        && !hasString(pkg.usesLibraries, changingPkg.libraryNames)
-                        && !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)
-                        && !ArrayUtils.contains(pkg.usesStaticLibraries,
-                                changingPkg.staticSharedLibName)) {
+                        && !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
+                        && !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
+                        && !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
+                                changingPkg.getStaticSharedLibName())) {
                     continue;
                 }
                 if (resultList == null) {
@@ -10090,8 +10061,8 @@
                     if (descendants == null) {
                         descendants = new ArraySet<>();
                     }
-                    if (!descendants.contains(pkg.packageName)) {
-                        descendants.add(pkg.packageName);
+                    if (!descendants.contains(pkg.getPackageName())) {
+                        descendants.add(pkg.getPackageName());
                         needsUpdating.add(pkg);
                     }
                 }
@@ -10106,8 +10077,9 @@
                     if (!pkg.isSystem() || pkg.isUpdatedSystemApp()) {
                         final int flags = pkg.isUpdatedSystemApp()
                                 ? PackageManager.DELETE_KEEP_DATA : 0;
-                        deletePackageLIF(pkg.packageName, null, true, mUserManager.getUserIds(),
-                                flags , null, true, null);
+                        deletePackageLIF(pkg.getPackageName(), null, true,
+                                mUserManager.getUserIds(), flags, null,
+                                true, null);
                     }
                     Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
                 }
@@ -10117,43 +10089,15 @@
     }
 
     @GuardedBy({"mInstallLock", "mLock"})
-    private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
+    private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
-        // If the package has children and this is the first dive in the function
-        // we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
-        // whether all packages (parent and children) would be successfully scanned
-        // before the actual scan since scanning mutates internal state and we want
-        // to atomically install the package and its children.
-        if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
-            if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
-                scanFlags |= SCAN_CHECK_ONLY;
-            }
-        } else {
-            scanFlags &= ~SCAN_CHECK_ONLY;
-        }
-
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        final List<ScanResult> scanResults = new ArrayList<>(1 + childCount);
         try {
-            // Scan the parent
-            scanResults.add(scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user));
-            // Scan the children
-            for (int i = 0; i < childCount; i++) {
-                PackageParser.Package childPkg = pkg.childPackages.get(i);
-                scanResults.add(scanPackageNewLI(childPkg, parseFlags,
-                        scanFlags, currentTime, user));
-            }
+            return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
-
-        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
-        }
-
-        return scanResults;
     }
 
     /** The result of a package scan. */
@@ -10200,9 +10144,9 @@
     @VisibleForTesting
     static class ScanRequest {
         /** The parsed package */
-        @NonNull public final PackageParser.Package pkg;
+        @NonNull public final ParsedPackage parsedPackage;
         /** The package this package replaces */
-        @Nullable public final PackageParser.Package oldPkg;
+        @Nullable public final AndroidPackage oldPkg;
         /** Shared user settings, if the package has a shared user */
         @Nullable public final SharedUserSetting sharedUserSetting;
         /**
@@ -10226,9 +10170,9 @@
         /** Whether or not the platform package is being scanned */
         public final boolean isPlatformPackage;
         public ScanRequest(
-                @NonNull PackageParser.Package pkg,
+                @NonNull ParsedPackage parsedPackage,
                 @Nullable SharedUserSetting sharedUserSetting,
-                @Nullable PackageParser.Package oldPkg,
+                @Nullable AndroidPackage oldPkg,
                 @Nullable PackageSetting pkgSetting,
                 @Nullable PackageSetting disabledPkgSetting,
                 @Nullable PackageSetting originalPkgSetting,
@@ -10237,7 +10181,7 @@
                 @ScanFlags int scanFlags,
                 boolean isPlatformPackage,
                 @Nullable UserHandle user) {
-            this.pkg = pkg;
+            this.parsedPackage = parsedPackage;
             this.oldPkg = oldPkg;
             this.pkgSetting = pkgSetting;
             this.sharedUserSetting = sharedUserSetting;
@@ -10270,7 +10214,7 @@
      */
     private @ScanFlags int adjustScanFlags(@ScanFlags int scanFlags,
             PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
-            PackageParser.Package pkg) {
+            AndroidPackage pkg) {
 
         // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
         // the correct isSystem value now that we don't disable system packages before scan.
@@ -10322,12 +10266,14 @@
                 && SystemProperties.getInt("ro.vndk.version", 28) < 28;
         if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
                 && !pkg.isPrivileged()
-                && (pkg.mSharedUserId != null)
+                && (pkg.getSharedUserId() != null)
                 && !skipVendorPrivilegeScan) {
             SharedUserSetting sharedUserSetting = null;
             try {
-                sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, false);
-            } catch (PackageManagerException ignore) {}
+                sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+                        0, false);
+            } catch (PackageManagerException ignore) {
+            }
             if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
                 // Exempt SharedUsers signed with the platform key.
                 // TODO(b/72378145) Fix this exemption. Force signature apps
@@ -10336,7 +10282,8 @@
                 synchronized (mLock) {
                     PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
                     if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
-                                pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
+                            pkg.getSigningDetails().signatures)
+                            != PackageManager.SIGNATURE_MATCH)) {
                         scanFlags |= SCAN_AS_PRIVILEGED;
                     }
                 }
@@ -10351,46 +10298,50 @@
     // method. Also, we need to solve the problem of potentially creating a new shared user
     // setting. That can probably be done later and patch things up after the fact.
     @GuardedBy({"mInstallLock", "mLock"})
-    private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
+    private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
 
-        final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
-        final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+        final String renamedPkgName = mSettings.getRenamedPackageLPr(
+                parsedPackage.getRealPackage());
+        final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
         if (realPkgName != null) {
-            ensurePackageRenamed(pkg, renamedPkgName);
+            ensurePackageRenamed(parsedPackage, renamedPkgName);
         }
-        final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
-        final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+        final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
+                renamedPkgName);
+        final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());
         final PackageSetting disabledPkgSetting =
-                mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+                mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
 
-        if (mTransferedPackages.contains(pkg.packageName)) {
-            Slog.w(TAG, "Package " + pkg.packageName
+        if (mTransferedPackages.contains(parsedPackage.getPackageName())) {
+            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
                     + " was transferred to another, but its .apk remains");
         }
 
-        scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
+        scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
         synchronized (mLock) {
-            applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
-            assertPackageIsValid(pkg, parseFlags, scanFlags);
+            applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage);
+            assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
 
             SharedUserSetting sharedUserSetting = null;
-            if (pkg.mSharedUserId != null) {
+            if (parsedPackage.getSharedUserId() != null) {
                 // SIDE EFFECTS; may potentially allocate a new shared user
-                sharedUserSetting = mSettings.getSharedUserLPw(
-                        pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+                sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+                        0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
                 if (DEBUG_PACKAGE_SCANNING) {
                     if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
-                        Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+                        Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
                                 + " (uid=" + sharedUserSetting.userId + "):"
                                 + " packages=" + sharedUserSetting.packages);
                 }
             }
-            final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,
+            String platformPackageName = mPlatformPackage == null
+                    ? null : mPlatformPackage.getPackageName();
+            final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
                     pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
                     originalPkgSetting, realPkgName, parseFlags, scanFlags,
-                    (pkg == mPlatformPackage), user);
+                    Objects.equals(parsedPackage.getPackageName(), platformPackageName), user);
             return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
         }
     }
@@ -10433,11 +10384,18 @@
      * possible and the system is not left in an inconsistent state.
      */
     @GuardedBy({"mLock", "mInstallLock"})
-    private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) {
+    private AndroidPackage commitReconciledScanResultLocked(
+            @NonNull ReconciledPackage reconciledPkg) {
         final ScanResult result = reconciledPkg.scanResult;
         final ScanRequest request = result.request;
-        final PackageParser.Package pkg = request.pkg;
-        final PackageParser.Package oldPkg = request.oldPkg;
+        // TODO(b/135203078): Move this even further away
+        ParsedPackage parsedPackage = request.parsedPackage;
+        if ("android".equals(parsedPackage.getPackageName())) {
+            // TODO(b/135203078): Move this to initial parse
+            parsedPackage.setVersionCode(mSdkVersion)
+                    .setVersionCodeMajor(0);
+        }
+        final AndroidPackage oldPkg = request.oldPkg;
         final @ParseFlags int parseFlags = request.parseFlags;
         final @ScanFlags int scanFlags = request.scanFlags;
         final PackageSetting oldPkgSetting = request.oldPkgSetting;
@@ -10454,13 +10412,11 @@
         if (result.existingSettingCopied) {
             pkgSetting = request.pkgSetting;
             pkgSetting.updateFrom(result.pkgSetting);
-            pkg.mExtras = pkgSetting;
         } else {
             pkgSetting = result.pkgSetting;
             if (originalPkgSetting != null) {
-                mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
-            }
-            if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
+                mSettings.addRenamedPackageLPw(parsedPackage.getPackageName(),
+                        originalPkgSetting.name);
                 mTransferedPackages.add(originalPkgSetting.name);
             }
         }
@@ -10473,12 +10429,13 @@
         // We need to have this here because addUserToSettingLPw() is sometimes responsible
         // for creating the application ID. If we did this earlier, we would be saving the
         // correct ID.
-        pkg.applicationInfo.uid = pkgSetting.appId;
+        parsedPackage.setUid(pkgSetting.appId);
+        final AndroidPackage pkg = parsedPackage.hideAsFinal();
 
         mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
 
-        if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realPkgName != null) {
-            mTransferedPackages.add(pkg.packageName);
+        if (realPkgName != null) {
+            mTransferedPackages.add(pkg.getPackageName());
         }
 
         if (reconciledPkg.collectedSharedLibraryInfos != null) {
@@ -10487,7 +10444,7 @@
 
         final KeySetManagerService ksms = mSettings.mKeySetManagerService;
         if (reconciledPkg.removeAppKeySetData) {
-            ksms.removeAppKeySetDataLPw(pkg.packageName);
+            ksms.removeAppKeySetDataLPw(pkg.getPackageName());
         }
         if (reconciledPkg.sharedUserSignaturesChanged) {
             pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
@@ -10495,17 +10452,17 @@
         }
         pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
 
-        if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
+        if (pkg.getAdoptPermissions() != null) {
             // This package wants to adopt ownership of permissions from
             // another package.
-            for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
-                final String origName = pkg.mAdoptPermissions.get(i);
+            for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) {
+                final String origName = pkg.getAdoptPermissions().get(i);
                 final PackageSetting orig = mSettings.getPackageLPr(origName);
                 if (orig != null) {
                     if (verifyPackageUpdateLPr(orig, pkg)) {
                         Slog.i(TAG, "Adopting permissions from " + origName + " to "
-                                + pkg.packageName);
-                        mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
+                                + pkg.getPackageName());
+                        mSettings.mPermissions.transferPermissions(origName, pkg.getPackageName());
                     }
                 }
             }
@@ -10522,21 +10479,15 @@
             }
         }
 
-        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            if (oldPkgSetting != null) {
-                synchronized (mLock) {
-                    mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
-                }
-            }
-        } else {
-            final int userId = user == null ? 0 : user.getIdentifier();
-            // Modify state for the given package setting
-            commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
-                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
-            if (pkgSetting.getInstantApp(userId)) {
-                mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
-            }
+        final int userId = user == null ? 0 : user.getIdentifier();
+        // Modify state for the given package setting
+        commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
+                (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
+        if (pkgSetting.getInstantApp(userId)) {
+            mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
         }
+
+        return pkg;
     }
 
     /**
@@ -10544,18 +10495,19 @@
      * <p>This may differ from the package's actual name if the application has already
      * been installed under one of this package's original names.
      */
-    private static @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg,
+    private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
             @Nullable String renamedPkgName) {
         if (isPackageRenamed(pkg, renamedPkgName)) {
-            return pkg.mRealPackage;
+            return pkg.getRealPackage();
         }
         return null;
     }
 
     /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
-    private static boolean isPackageRenamed(@NonNull PackageParser.Package pkg,
+    private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
             @Nullable String renamedPkgName) {
-        return pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(renamedPkgName);
+        return pkg.getOriginalPackages() != null
+                && pkg.getOriginalPackages().contains(renamedPkgName);
     }
 
     /**
@@ -10566,14 +10518,14 @@
      * shared user [if any].
      */
     @GuardedBy("mLock")
-    private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
+    private @Nullable PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
             @Nullable String renamedPkgName) {
         if (!isPackageRenamed(pkg, renamedPkgName)) {
             return null;
         }
-        for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) {
+        for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
             final PackageSetting originalPs =
-                    mSettings.getPackageLPr(pkg.mOriginalPackages.get(i));
+                    mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
             if (originalPs != null) {
                 // the package is already installed under its original name...
                 // but, should we use it?
@@ -10581,18 +10533,18 @@
                     // the new package is incompatible with the original
                     continue;
                 } else if (originalPs.sharedUser != null) {
-                    if (!originalPs.sharedUser.name.equals(pkg.mSharedUserId)) {
+                    if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
                         // the shared user id is incompatible with the original
                         Slog.w(TAG, "Unable to migrate data from " + originalPs.name
-                                + " to " + pkg.packageName + ": old uid "
+                                + " to " + pkg.getPackageName() + ": old uid "
                                 + originalPs.sharedUser.name
-                                + " differs from " + pkg.mSharedUserId);
+                                + " differs from " + pkg.getSharedUserId());
                         continue;
                     }
                     // TODO: Add case when shared user id is added [b/28144775]
                 } else {
                     if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
-                            + pkg.packageName + " to old name " + originalPs.name);
+                            + pkg.getPackageName() + " to old name " + originalPs.name);
                 }
                 return originalPs;
             }
@@ -10605,19 +10557,19 @@
      * <p>When we've already installed the package under an original name, update
      * the new package so we can continue to have the old name.
      */
-    private static void ensurePackageRenamed(@NonNull PackageParser.Package pkg,
+    private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
             @NonNull String renamedPackageName) {
-        if (pkg.mOriginalPackages == null
-                || !pkg.mOriginalPackages.contains(renamedPackageName)
-                || pkg.packageName.equals(renamedPackageName)) {
+        if (parsedPackage.getOriginalPackages() == null
+                || !parsedPackage.getOriginalPackages().contains(renamedPackageName)
+                || parsedPackage.getPackageName().equals(renamedPackageName)) {
             return;
         }
-        pkg.setPackageName(renamedPackageName);
+        parsedPackage.setPackageName(renamedPackageName);
     }
 
     /**
      * Applies the adjusted ABI calculated by
-     * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, PackageParser.Package)} to all
+     * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
      * relevant packages and settings.
      * @param sharedUserSetting The {@code SharedUserSetting} to adjust
      * @param scannedPackage the package being scanned or null
@@ -10625,22 +10577,20 @@
      * @return the list of code paths that belong to packages that had their ABIs adjusted.
      */
     private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
-            PackageParser.Package scannedPackage, String adjustedAbi) {
+            ParsedPackage scannedPackage, String adjustedAbi) {
         if (scannedPackage != null)  {
-            scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
+            scannedPackage.setPrimaryCpuAbi(adjustedAbi);
         }
         List<String> changedAbiCodePath = null;
         for (PackageSetting ps : sharedUserSetting.packages) {
-            if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
+            if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
                 if (ps.primaryCpuAbiString != null) {
                     continue;
                 }
 
                 ps.primaryCpuAbiString = adjustedAbi;
-                if (ps.pkg != null && ps.pkg.applicationInfo != null
-                        && !TextUtils.equals(
-                        adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
-                    ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
+                if (ps.pkg != null && !TextUtils.equals(adjustedAbi, ps.pkg.getPrimaryCpuAbi())) {
+                    ps.pkg.mutate().setPrimaryCpuAbi(adjustedAbi);
                     if (DEBUG_ABI_SELECTION) {
                         Slog.i(TAG,
                                 "Adjusting ABI for " + ps.name + " to " + adjustedAbi
@@ -10680,7 +10630,7 @@
             throws PackageManagerException {
         final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
         final UserManagerInternal userManager = injector.getUserManagerInternal();
-        final PackageParser.Package pkg = request.pkg;
+        ParsedPackage parsedPackage = request.parsedPackage;
         PackageSetting pkgSetting = request.pkgSetting;
         final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
         final PackageSetting originalPkgSetting = request.originalPkgSetting;
@@ -10695,13 +10645,12 @@
 
         if (DEBUG_PACKAGE_SCANNING) {
             if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
-                Log.d(TAG, "Scanning package " + pkg.packageName);
+                Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
         }
 
         // Initialize package source and resource directories
-        final File scanFile = new File(pkg.codePath);
-        final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
-        final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
+        final File destCodeFile = new File(parsedPackage.getAppInfoCodePath());
+        final File destResourceFile = new File(parsedPackage.getAppInfoResourcePath());
 
         // We keep references to the derived CPU Abis from settings in oder to reuse
         // them in the case where we're not upgrading or booting for the first time.
@@ -10720,7 +10669,7 @@
 
         if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
-                    "Package " + pkg.packageName + " shared user changed from "
+                    "Package " + parsedPackage.getPackageName() + " shared user changed from "
                             + (pkgSetting.sharedUser != null
                             ? pkgSetting.sharedUser.name : "<nothing>")
                             + " to "
@@ -10730,30 +10679,28 @@
         }
 
         String[] usesStaticLibraries = null;
-        if (pkg.usesStaticLibraries != null) {
-            usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
-            pkg.usesStaticLibraries.toArray(usesStaticLibraries);
+        if (parsedPackage.getUsesStaticLibraries() != null) {
+            usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+            parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
         }
         final boolean createNewPackage = (pkgSetting == null);
         if (createNewPackage) {
-            final String parentPackageName = (pkg.parentPackage != null)
-                    ? pkg.parentPackage.packageName : null;
             final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
             final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
             // REMOVE SharedUserSetting from method; update in a separate call
-            pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
-                    disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
-                    destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
-                    pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
-                    pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
-                    user, true /*allowInstall*/, instantApp, virtualPreload,
-                    parentPackageName, pkg.getChildPackageNames(),
-                    UserManagerService.getInstance(), usesStaticLibraries,
-                    pkg.usesStaticLibrariesVersions);
+            pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+                    originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
+                    parsedPackage.getPrimaryCpuAbi(), parsedPackage.getSecondaryCpuAbi(),
+                    parsedPackage.getVersionCode(), parsedPackage.getFlags(),
+                    parsedPackage.getPrivateFlags(), user, true /*allowInstall*/, instantApp,
+                    virtualPreload, UserManagerService.getInstance(), usesStaticLibraries,
+                    parsedPackage.getUsesStaticLibrariesVersions());
         } else {
             // make a deep copy to avoid modifying any existing system state.
             pkgSetting = new PackageSetting(pkgSetting);
-            pkgSetting.pkg = pkg;
+            // TODO(b/135203078): Remove entirely. Set package directly.
+            parsedPackage.setPackageSettingCallback(pkgSetting);
 
             // REMOVE SharedUserSetting from method; update in a separate call.
             //
@@ -10761,18 +10708,18 @@
             // secondaryCpuAbi are not known at this point so we always update them
             // to null here, only to reset them at a later point.
             Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
-                    destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir,
-                    pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
-                    pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
-                    pkg.getChildPackageNames(), UserManagerService.getInstance(),
-                    usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
+                    parsedPackage.getPrimaryCpuAbi(), parsedPackage.getSecondaryCpuAbi(),
+                    parsedPackage.getFlags(), parsedPackage.getPrivateFlags(),
+                    UserManagerService.getInstance(),
+                    usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions());
         }
         if (createNewPackage && originalPkgSetting != null) {
             // This is the initial transition from the original package, so,
             // fix up the new package's name now. We must do this after looking
             // up the package under its new name, so getPackageLP takes care of
             // fiddling things correctly.
-            pkg.setPackageName(originalPkgSetting.name);
+            parsedPackage.setPackageName(originalPkgSetting.name);
 
             // File a report about this.
             String msg = "New package " + pkgSetting.realName
@@ -10791,7 +10738,7 @@
         if (disabledPkgSetting != null
                 || (0 != (scanFlags & SCAN_NEW_INSTALL)
                 && pkgSetting != null && pkgSetting.isSystem())) {
-            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+            parsedPackage.mutate().setUpdatedSystemApp(true);
         }
 
         // Apps which share a sharedUserId must be placed in the same selinux domain. If this
@@ -10803,68 +10750,72 @@
         // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
         // ensures that all packages continue to run in the same selinux domain.
         final int targetSdkVersion =
-            ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
-            sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
+                ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
+                        sharedUserSetting.seInfoTargetSdkVersion
+                        : parsedPackage.getTargetSdkVersion();
         // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
         // They currently can be if the sharedUser apps are signed with the platform key.
         final boolean isPrivileged = (sharedUserSetting != null) ?
-            sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+                sharedUserSetting.isPrivileged() | parsedPackage.isPrivileged()
+                : parsedPackage.isPrivileged();
 
-        pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
-                targetSdkVersion);
-        pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
-                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
-
-        pkg.mExtras = pkgSetting;
-        pkg.applicationInfo.processName = fixProcessName(
-                pkg.applicationInfo.packageName,
-                pkg.applicationInfo.processName);
+        parsedPackage.setSeInfo(
+                SELinuxMMAC.getSeInfo(parsedPackage, isPrivileged, targetSdkVersion))
+                .setSeInfoUser(
+                        SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
+                                userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)))
+                .setProcessName(fixProcessName(
+                        parsedPackage.getPackageName(),
+                        parsedPackage.getProcessName()));
 
         if (!isPlatformPackage) {
             // Get all of our default paths setup
-            pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
+            parsedPackage.initForUser(UserHandle.USER_SYSTEM);
         }
 
-        final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
+        final String cpuAbiOverride = deriveAbiOverride(parsedPackage.getCpuAbiOverride(),
+                pkgSetting);
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
             if (needToDeriveAbi) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
-                final boolean extractNativeLibs = !pkg.isLibrary();
+                final boolean extractNativeLibs = !parsedPackage.isLibrary();
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
-                        packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
-                derivedAbi.first.applyTo(pkg);
-                derivedAbi.second.applyTo(pkg);
+                        packageAbiHelper.derivePackageAbi(parsedPackage, cpuAbiOverride,
+                                extractNativeLibs);
+                derivedAbi.first.applyTo(parsedPackage);
+                derivedAbi.second.applyTo(parsedPackage);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
                 // Some system apps still use directory structure for native libraries
                 // in which case we might end up not detecting abi solely based on apk
                 // structure. Try to detect abi based on directory structure.
-                if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
-                        pkg.applicationInfo.primaryCpuAbi == null) {
+                if (isSystemApp(parsedPackage) && !parsedPackage.isUpdatedSystemApp() &&
+                        parsedPackage.getPrimaryCpuAbi() == null) {
                     final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
-                            pkg);
-                    abis.applyTo(pkg);
+                            parsedPackage);
+                    abis.applyTo(parsedPackage);
                     abis.applyTo(pkgSetting);
                     final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                            packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
-                    nativeLibraryPaths.applyTo(pkg);
+                            packageAbiHelper.getNativeLibraryPaths(parsedPackage,
+                                    sAppLib32InstallDir);
+                    nativeLibraryPaths.applyTo(parsedPackage);
                 }
             } else {
                 // This is not a first boot or an upgrade, don't bother deriving the
                 // ABI during the scan. Instead, trust the value that was stored in the
                 // package setting.
-                pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
-                pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
+                parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+                        .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
 
                 final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                        packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
-                nativeLibraryPaths.applyTo(pkg);
+                        packageAbiHelper.getNativeLibraryPaths(parsedPackage, sAppLib32InstallDir);
+                nativeLibraryPaths.applyTo(parsedPackage);
 
                 if (DEBUG_ABI_SELECTION) {
                     Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
-                            pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
-                            pkg.applicationInfo.secondaryCpuAbi);
+                            parsedPackage.getPackageName() + " " + parsedPackage.getPrimaryCpuAbi()
+                            + ", " + parsedPackage.getSecondaryCpuAbi());
                 }
             }
         } else {
@@ -10872,8 +10823,8 @@
                 // We haven't run dex-opt for this move (since we've moved the compiled output too)
                 // but we already have this packages package info in the PackageSetting. We just
                 // use that and derive the native library path based on the new codepath.
-                pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
-                pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
+                parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
+                        .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
             }
 
             // Set native library paths again. For moves, the path will be updated based on the
@@ -10881,8 +10832,8 @@
             // ABIs we determined during compilation, but the path will depend on the final
             // package path (after the rename away from the stage path).
             final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                    packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir);
-            nativeLibraryPaths.applyTo(pkg);
+                    packageAbiHelper.getNativeLibraryPaths(parsedPackage, sAppLib32InstallDir);
+            nativeLibraryPaths.applyTo(parsedPackage);
         }
 
         // This is a special case for the "system" package, where the ABI is
@@ -10890,8 +10841,8 @@
         // of this ABI so that we can deal with "normal" applications that run under
         // the same UID correctly.
         if (isPlatformPackage) {
-            pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
-                    Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+            parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?
+                    Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
         }
 
         // If there's a mismatch between the abi-override in the package setting
@@ -10899,34 +10850,34 @@
         // would've already compiled the app without taking the package setting into
         // account.
         if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
-            if (cpuAbiOverride == null && pkg.packageName != null) {
+            if (cpuAbiOverride == null && parsedPackage.getPackageName() != null) {
                 Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
-                        " for package " + pkg.packageName);
+                        " for package " + parsedPackage.getPackageName());
             }
         }
 
-        pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
-        pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
+        pkgSetting.primaryCpuAbiString = parsedPackage.getPrimaryCpuAbi();
+        pkgSetting.secondaryCpuAbiString = parsedPackage.getSecondaryCpuAbi();
         pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
 
         // Copy the derived override back to the parsed package, so that we can
         // update the package settings accordingly.
-        pkg.cpuAbiOverride = cpuAbiOverride;
+        parsedPackage.setCpuAbiOverride(cpuAbiOverride);
 
         if (DEBUG_ABI_SELECTION) {
-            Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.packageName
-                    + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
-                    + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
+            Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+                    + " to root=" + parsedPackage.getNativeLibraryRootDir() + ", isa="
+                    + parsedPackage.isNativeLibraryRootRequiresIsa());
         }
 
         // Push the derived path down into PackageSettings so we know what to
         // clean up at uninstall time.
-        pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
+        pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
 
         if (DEBUG_ABI_SELECTION) {
-            Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
-                    " primary=" + pkg.applicationInfo.primaryCpuAbi +
-                    " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
+            Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are" +
+                    " primary=" + parsedPackage.getPrimaryCpuAbi() +
+                    " secondary=" + parsedPackage.getSecondaryCpuAbi());
         }
 
         if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
@@ -10936,22 +10887,20 @@
             // We also do this *before* we perform dexopt on this package, so that
             // we can avoid redundant dexopts, and also to make sure we've got the
             // code and package path correct.
-            changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, pkg,
+            changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
                     packageAbiHelper.getAdjustedAbiForSharedUser(
-                            pkgSetting.sharedUser.packages, pkg));
+                            pkgSetting.sharedUser.packages, parsedPackage));
         }
 
-        if (isUnderFactoryTest && pkg.requestedPermissions.contains(
-                android.Manifest.permission.FACTORY_TEST)) {
-            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
-        }
+        parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+                .contains(android.Manifest.permission.FACTORY_TEST));
 
-        if (isSystemApp(pkg)) {
+        if (isSystemApp(parsedPackage)) {
             pkgSetting.isOrphaned = true;
         }
 
         // Take care of first install / last update times.
-        final long scanFileTime = getLastModifiedTime(pkg);
+        final long scanFileTime = getLastModifiedTime(parsedPackage);
         if (currentTime != 0) {
             if (pkgSetting.firstInstallTime == 0) {
                 pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
@@ -10969,32 +10918,33 @@
             }
         }
         pkgSetting.setTimeStamp(scanFileTime);
-
-        pkgSetting.pkg = pkg;
-        pkgSetting.pkgFlags = pkg.applicationInfo.flags;
-        if (pkg.getLongVersionCode() != pkgSetting.versionCode) {
-            pkgSetting.versionCode = pkg.getLongVersionCode();
+        // TODO(b/135203078): Remove, move to constructor
+        parsedPackage.setPackageSettingCallback(pkgSetting);
+        pkgSetting.pkgFlags = parsedPackage.getFlags();
+        if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
+            pkgSetting.versionCode = parsedPackage.getLongVersionCode();
         }
         // Update volume if needed
-        final String volumeUuid = pkg.applicationInfo.volumeUuid;
+        final String volumeUuid = parsedPackage.getApplicationInfoVolumeUuid();
         if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
             Slog.i(PackageManagerService.TAG,
                     "Update" + (pkgSetting.isSystem() ? " system" : "")
-                    + " package " + pkg.packageName
+                    + " package " + parsedPackage.getPackageName()
                     + " volume from " + pkgSetting.volumeUuid
                     + " to " + volumeUuid);
             pkgSetting.volumeUuid = volumeUuid;
         }
 
         SharedLibraryInfo staticSharedLibraryInfo = null;
-        if (!TextUtils.isEmpty(pkg.staticSharedLibName)) {
-            staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(pkg);
+        if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+            staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(parsedPackage);
         }
         List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
-        if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
-            dynamicSharedLibraryInfos = new ArrayList<>(pkg.libraryNames.size());
-            for (String name : pkg.libraryNames) {
-                dynamicSharedLibraryInfos.add(SharedLibraryInfo.createForDynamic(pkg, name));
+        if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+            dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+            for (String name : parsedPackage.getLibraryNames()) {
+                dynamicSharedLibraryInfos.add(
+                        SharedLibraryInfo.createForDynamic(parsedPackage, name));
             }
         }
 
@@ -11030,22 +10980,21 @@
      *
      * @throws PackageManagerException If bytecode could not be found when it should exist
      */
-    private static void assertCodePolicy(PackageParser.Package pkg)
+    private static void assertCodePolicy(AndroidPackage pkg)
             throws PackageManagerException {
-        final boolean shouldHaveCode =
-                (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
-        if (shouldHaveCode && !apkHasCode(pkg.baseCodePath)) {
+        final boolean shouldHaveCode = (pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0;
+        if (shouldHaveCode && !apkHasCode(pkg.getBaseCodePath())) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                    "Package " + pkg.baseCodePath + " code is missing");
+                    "Package " + pkg.getBaseCodePath() + " code is missing");
         }
 
-        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-            for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+        if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+            for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
                 final boolean splitShouldHaveCode =
-                        (pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
-                if (splitShouldHaveCode && !apkHasCode(pkg.splitCodePaths[i])) {
+                        (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+                if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
                     throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                            "Package " + pkg.splitCodePaths[i] + " code is missing");
+                            "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
                 }
             }
         }
@@ -11058,118 +11007,59 @@
      * Implementation detail: This method must NOT have any side effect. It would
      * ideally be static, but, it requires locks to read system state.
      */
-    private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags, PackageParser.Package platformPkg) {
+    private static void applyPolicy(ParsedPackage parsedPackage, final @ParseFlags int parseFlags,
+            final @ScanFlags int scanFlags, AndroidPackage platformPkg) {
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
-            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
-            if (pkg.applicationInfo.isDirectBootAware()) {
-                // we're direct boot aware; set for all components
-                for (PackageParser.Service s : pkg.services) {
-                    s.info.directBootAware = true;
-                }
-                for (PackageParser.Provider p : pkg.providers) {
-                    p.info.directBootAware = true;
-                }
-                for (PackageParser.Activity a : pkg.activities) {
-                    a.info.directBootAware = true;
-                }
-                for (PackageParser.Activity r : pkg.receivers) {
-                    r.info.directBootAware = true;
-                }
+            parsedPackage.setSystem(true);
+            // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+            //  is set during parse.
+            if (parsedPackage.isDirectBootAware()) {
+                parsedPackage.setAllComponentsDirectBootAware(true);
             }
-            if (compressedFileExists(pkg.codePath)) {
-                pkg.isStub = true;
+            if (compressedFileExists(parsedPackage.getCodePath())) {
+                parsedPackage.setIsStub(true);
             }
         } else {
-            // non system apps can't be flagged as core
-            pkg.coreApp = false;
-            // clear flags not applicable to regular apps
-            pkg.applicationInfo.flags &=
-                    ~ApplicationInfo.FLAG_PERSISTENT;
-            pkg.applicationInfo.privateFlags &=
-                    ~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
-            pkg.applicationInfo.privateFlags &=
-                    ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
-            // cap permission priorities
-            if (pkg.permissionGroups != null && pkg.permissionGroups.size() > 0) {
-                for (int i = pkg.permissionGroups.size() - 1; i >= 0; --i) {
-                    pkg.permissionGroups.get(i).info.priority = 0;
-                }
-            }
+            parsedPackage
+                    // non system apps can't be flagged as core
+                    .setCoreApp(false)
+                    // clear flags not applicable to regular apps
+                    .setPersistent(false)
+                    .setDefaultToDeviceProtectedStorage(false)
+                    .setDirectBootAware(false)
+                    // non system apps can't have permission priority
+                    .capPermissionPriorities();
         }
         if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
-            // clear protected broadcasts
-            pkg.protectedBroadcasts = null;
-            // ignore export request for single user receivers
-            if (pkg.receivers != null) {
-                for (int i = pkg.receivers.size() - 1; i >= 0; --i) {
-                    final PackageParser.Activity receiver = pkg.receivers.get(i);
-                    if ((receiver.info.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                        receiver.info.exported = false;
-                    }
-                }
-            }
-            // ignore export request for single user services
-            if (pkg.services != null) {
-                for (int i = pkg.services.size() - 1; i >= 0; --i) {
-                    final PackageParser.Service service = pkg.services.get(i);
-                    if ((service.info.flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
-                        service.info.exported = false;
-                    }
-                }
-            }
-            // ignore export request for single user providers
-            if (pkg.providers != null) {
-                for (int i = pkg.providers.size() - 1; i >= 0; --i) {
-                    final PackageParser.Provider provider = pkg.providers.get(i);
-                    if ((provider.info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0) {
-                        provider.info.exported = false;
-                    }
-                }
-            }
+            parsedPackage
+                    .clearProtectedBroadcasts()
+                    .markNotActivitiesAsNotExportedIfSingleUser();
         }
 
-        if ((scanFlags & SCAN_AS_PRIVILEGED) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-        }
-
-        if ((scanFlags & SCAN_AS_OEM) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
-        }
-
-        if ((scanFlags & SCAN_AS_VENDOR) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VENDOR;
-        }
-
-        if ((scanFlags & SCAN_AS_PRODUCT) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT;
-        }
-
-        if ((scanFlags & SCAN_AS_SYSTEM_EXT) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
-        }
-
-        if ((scanFlags & SCAN_AS_ODM) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ODM;
-        }
+        parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+                .setOem((scanFlags & SCAN_AS_OEM) != 0)
+                .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+                .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+                .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+                .setOdm((scanFlags & SCAN_AS_ODM) != 0);
 
         // Check if the package is signed with the same key as the platform package.
-        if (PLATFORM_PACKAGE_NAME.equals(pkg.packageName) ||
-                (platformPkg != null && compareSignatures(
-                        platformPkg.mSigningDetails.signatures,
-                        pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH)) {
-            pkg.applicationInfo.privateFlags |=
-                ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
-        }
+        parsedPackage.setSignedWithPlatformKey(
+                (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+                        || (platformPkg != null && compareSignatures(
+                        platformPkg.getSigningDetails().signatures,
+                        parsedPackage.getSigningDetails().signatures
+                ) == PackageManager.SIGNATURE_MATCH))
+        );
 
-        if (!isSystemApp(pkg)) {
+        if (!isSystemApp(parsedPackage)) {
             // Only system apps can use these features.
-            pkg.mOriginalPackages = null;
-            pkg.mRealPackage = null;
-            pkg.mAdoptPermissions = null;
+            parsedPackage.clearOriginalPackages()
+                    .setRealPackage(null)
+                    .clearAdoptPermissions();
         }
 
-        PackageBackwardCompatibility.modifySharedLibraries(pkg);
+        PackageBackwardCompatibility.modifySharedLibraries(parsedPackage);
     }
 
     private static @NonNull <T> T assertNotNull(@Nullable T object, String message)
@@ -11189,15 +11079,15 @@
      *
      * @throws PackageManagerException If the package fails any of the validation checks
      */
-    private void assertPackageIsValid(PackageParser.Package pkg, final @ParseFlags int parseFlags,
+    private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
             final @ScanFlags int scanFlags)
                     throws PackageManagerException {
         if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
             assertCodePolicy(pkg);
         }
 
-        if (pkg.applicationInfo.getCodePath() == null ||
-                pkg.applicationInfo.getResourcePath() == null) {
+        if (pkg.getAppInfoCodePath() == null ||
+                pkg.getAppInfoResourcePath() == null) {
             // Bail out. The resource and code paths haven't been set.
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     "Code and resource paths haven't been set correctly");
@@ -11208,9 +11098,10 @@
         final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
         final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
         if ((isUserInstall || isFirstBootOrUpgrade)
-                && mApexManager.isApexPackage(pkg.packageName)) {
+                && mApexManager.isApexPackage(pkg.getPackageName())) {
             throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                    pkg.packageName + " is an APEX package and can't be installed as an APK.");
+                    pkg.getPackageName()
+                            + " is an APEX package and can't be installed as an APK.");
         }
 
         // Make sure we're not adding any bogus keyset info
@@ -11219,11 +11110,11 @@
 
         synchronized (mLock) {
             // The special "android" package can only be defined once
-            if (pkg.packageName.equals("android")) {
+            if (pkg.getPackageName().equals("android")) {
                 if (mAndroidApplication != null) {
                     Slog.w(TAG, "*************************************************");
                     Slog.w(TAG, "Core android package being redefined.  Skipping.");
-                    Slog.w(TAG, " codePath=" + pkg.codePath);
+                    Slog.w(TAG, " codePath=" + pkg.getCodePath());
                     Slog.w(TAG, "*************************************************");
                     throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                             "Core android package being redefined.  Skipping.");
@@ -11231,23 +11122,24 @@
             }
 
             // A package name must be unique; don't allow duplicates
-            if ((scanFlags & SCAN_NEW_INSTALL) == 0 && mPackages.containsKey(pkg.packageName)) {
+            if ((scanFlags & SCAN_NEW_INSTALL) == 0
+                    && mPackages.containsKey(pkg.getPackageName())) {
                 throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                        "Application package " + pkg.packageName
+                        "Application package " + pkg.getPackageName()
                         + " already installed.  Skipping duplicate.");
             }
 
-            if (pkg.applicationInfo.isStaticSharedLibrary()) {
+            if (pkg.isStaticSharedLibrary()) {
                 // Static libs have a synthetic package name containing the version
                 // but we still want the base name to be unique.
                 if ((scanFlags & SCAN_NEW_INSTALL) == 0
-                        && mPackages.containsKey(pkg.manifestPackageName)) {
+                        && mPackages.containsKey(pkg.getManifestPackageName())) {
                     throw new PackageManagerException(
                             "Duplicate static shared lib provider package");
                 }
 
                 // Static shared libraries should have at least O target SDK
-                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+                if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
                     throw new PackageManagerException(
                             "Packages declaring static-shared libs must target O SDK or higher");
                 }
@@ -11260,73 +11152,67 @@
 
                 // Package declaring static a shared lib cannot be renamed since the package
                 // name is synthetic and apps can't code around package manager internals.
-                if (!ArrayUtils.isEmpty(pkg.mOriginalPackages)) {
+                if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
                     throw new PackageManagerException(
                             "Packages declaring static-shared libs cannot be renamed");
                 }
 
-                // Package declaring static a shared lib cannot declare child packages
-                if (!ArrayUtils.isEmpty(pkg.childPackages)) {
-                    throw new PackageManagerException(
-                            "Packages declaring static-shared libs cannot have child packages");
-                }
-
                 // Package declaring static a shared lib cannot declare dynamic libs
-                if (!ArrayUtils.isEmpty(pkg.libraryNames)) {
+                if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
                     throw new PackageManagerException(
                             "Packages declaring static-shared libs cannot declare dynamic libs");
                 }
 
                 // Package declaring static a shared lib cannot declare shared users
-                if (pkg.mSharedUserId != null) {
+                if (pkg.getSharedUserId() != null) {
                     throw new PackageManagerException(
                             "Packages declaring static-shared libs cannot declare shared users");
                 }
 
                 // Static shared libs cannot declare activities
-                if (!pkg.activities.isEmpty()) {
+                if (pkg.getActivities() != null && !pkg.getActivities().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare activities");
                 }
 
                 // Static shared libs cannot declare services
-                if (!pkg.services.isEmpty()) {
+                if (pkg.getServices() != null && !pkg.getServices().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare services");
                 }
 
                 // Static shared libs cannot declare providers
-                if (!pkg.providers.isEmpty()) {
+                if (pkg.getProviders() != null && !pkg.getProviders().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare content providers");
                 }
 
                 // Static shared libs cannot declare receivers
-                if (!pkg.receivers.isEmpty()) {
+                if (pkg.getReceivers() != null && !pkg.getReceivers().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare broadcast receivers");
                 }
 
                 // Static shared libs cannot declare permission groups
-                if (!pkg.permissionGroups.isEmpty()) {
+                if (pkg.getPermissionGroups() != null && !pkg.getPermissionGroups().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare permission groups");
                 }
 
                 // Static shared libs cannot declare permissions
-                if (!pkg.permissions.isEmpty()) {
+                if (pkg.getPermissions() != null && !pkg.getPermissions().isEmpty()) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare permissions");
                 }
 
                 // Static shared libs cannot declare protected broadcasts
-                if (pkg.protectedBroadcasts != null) {
+                if (pkg.getProtectedBroadcasts() != null) {
                     throw new PackageManagerException(
                             "Static shared libs cannot declare protected broadcasts");
                 }
 
                 // Static shared libs cannot be overlay targets
-                if (pkg.mOverlayTarget != null) {
+                if (pkg.getOverlayTarget() != null) {
                     throw new PackageManagerException(
                             "Static shared libs cannot be overlay targets");
                 }
@@ -11336,16 +11222,17 @@
                 long maxVersionCode = Long.MAX_VALUE;
 
                 LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
-                        pkg.staticSharedLibName);
+                        pkg.getStaticSharedLibName());
                 if (versionedLib != null) {
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
                         SharedLibraryInfo libInfo = versionedLib.valueAt(i);
                         final long libVersionCode = libInfo.getDeclaringPackage()
                                 .getLongVersionCode();
-                        if (libInfo.getLongVersion() <  pkg.staticSharedLibVersion) {
+                        if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
                             minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
-                        } else if (libInfo.getLongVersion() >  pkg.staticSharedLibVersion) {
+                        } else if (libInfo.getLongVersion()
+                                > pkg.getStaticSharedLibVersion()) {
                             maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
                         } else {
                             minVersionCode = maxVersionCode = libVersionCode;
@@ -11360,23 +11247,6 @@
                 }
             }
 
-            // Only privileged apps and updated privileged apps can add child packages.
-            if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
-                if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
-                    throw new PackageManagerException("Only privileged apps can add child "
-                            + "packages. Ignoring package " + pkg.packageName);
-                }
-                final int childCount = pkg.childPackages.size();
-                for (int i = 0; i < childCount; i++) {
-                    PackageParser.Package childPkg = pkg.childPackages.get(i);
-                    if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,
-                            childPkg.packageName)) {
-                        throw new PackageManagerException("Can't override child of "
-                                + "another disabled app. Ignoring package " + pkg.packageName);
-                    }
-                }
-            }
-
             // If we're only installing presumed-existing packages, require that the
             // scanned APK is both already known and at the path previously established
             // for it.  Previously unknown packages we pick up normally, but if we have an
@@ -11386,29 +11256,30 @@
             // to the user-installed location. If we don't allow this change, any newer,
             // user-installed version of the application will be ignored.
             if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
-                if (mExpectingBetter.containsKey(pkg.packageName)) {
+                if (mExpectingBetter.containsKey(pkg.getPackageName())) {
                     logCriticalInfo(Log.WARN,
-                            "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
+                            "Relax SCAN_REQUIRE_KNOWN requirement for package "
+                                    + pkg.getPackageName());
                 } else {
-                    PackageSetting known = mSettings.getPackageLPr(pkg.packageName);
+                    PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
                     if (known != null) {
                         if (DEBUG_PACKAGE_SCANNING) {
-                            Log.d(TAG, "Examining " + pkg.codePath
+                            Log.d(TAG, "Examining " + pkg.getCodePath()
                                     + " and requiring known paths " + known.codePathString
                                     + " & " + known.resourcePathString);
                         }
-                        if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
-                                || !pkg.applicationInfo.getResourcePath().equals(
+                        if (!pkg.getAppInfoCodePath().equals(known.codePathString)
+                                || !pkg.getAppInfoResourcePath().equals(
                                         known.resourcePathString)) {
                             throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
-                                    "Application package " + pkg.packageName
-                                    + " found at " + pkg.applicationInfo.getCodePath()
+                                    "Application package " + pkg.getPackageName()
+                                    + " found at " + pkg.getAppInfoCodePath()
                                     + " but expected at " + known.codePathString
                                     + "; ignoring.");
                         }
                     } else {
                         throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
-                                "Application package " + pkg.packageName
+                                "Application package " + pkg.getPackageName()
                                 + " not found; ignoring.");
                     }
                 }
@@ -11423,11 +11294,13 @@
             }
 
             // Verify that packages sharing a user with a privileged app are marked as privileged.
-            if (!pkg.isPrivileged() && (pkg.mSharedUserId != null)) {
+            if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
                 SharedUserSetting sharedUserSetting = null;
                 try {
-                    sharedUserSetting = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, 0, false);
-                } catch (PackageManagerException ignore) {}
+                    sharedUserSetting = mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+                            0, 0, false);
+                } catch (PackageManagerException ignore) {
+                }
                 if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
                     // Exempt SharedUsers signed with the platform key.
                     PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
@@ -11435,18 +11308,18 @@
                             != PackageParser.SigningDetails.UNKNOWN)
                             && (compareSignatures(
                                     platformPkgSetting.signatures.mSigningDetails.signatures,
-                                    pkg.mSigningDetails.signatures)
+                            pkg.getSigningDetails().signatures)
                                             != PackageManager.SIGNATURE_MATCH)) {
                         throw new PackageManagerException("Apps that share a user with a " +
                                 "privileged app must themselves be marked as privileged. " +
-                                pkg.packageName + " shares privileged user " +
-                                pkg.mSharedUserId + ".");
+                                pkg.getPackageName() + " shares privileged user " +
+                                pkg.getSharedUserId() + ".");
                     }
                 }
             }
 
             // Apply policies specific for runtime resource overlays (RROs).
-            if (pkg.mOverlayTarget != null) {
+            if (pkg.getOverlayTarget() != null) {
                 // System overlays have some restrictions on their use of the 'static' state.
                 if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
                     // We are scanning a system overlay. This can be the first scan of the
@@ -11454,54 +11327,62 @@
                     if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         // This must be an update to a system overlay.
                         final PackageSetting previousPkg = assertNotNull(
-                                mSettings.getPackageLPr(pkg.packageName),
+                                mSettings.getPackageLPr(pkg.getPackageName()),
                                 "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;
+                        AndroidPackage ppkg = previousPkg.pkg;
                         if (ppkg == null) {
                             try {
                                 final PackageParser pp = new PackageParser();
-                                ppkg = pp.parsePackage(previousPkg.codePath,
-                                        parseFlags | PackageParser.PARSE_IS_SYSTEM_DIR);
+                                // TODO(b/135203078): Do we really need to parse here? Maybe use
+                                //  a shortened path?
+                                ppkg = pp.parseParsedPackage(previousPkg.codePath,
+                                        parseFlags | PackageParser.PARSE_IS_SYSTEM_DIR,
+                                        false)
+                                        .hideAsFinal();
                             } catch (PackageParserException e) {
                                 Slog.w(TAG, "failed to parse " + previousPkg.codePath, e);
                             }
                         }
 
                         // Static overlays cannot be updated.
-                        if (ppkg != null && ppkg.mOverlayIsStatic) {
-                            throw new PackageManagerException("Overlay " + pkg.packageName +
-                                    " is static and cannot be upgraded.");
+                        if (ppkg != null && ppkg.isOverlayIsStatic()) {
+                            throw new PackageManagerException("Overlay "
+                                    + pkg.getPackageName()
+                                    + " is static and cannot be upgraded.");
                         // Non-static overlays cannot be converted to static overlays.
-                        } else if (pkg.mOverlayIsStatic) {
-                            throw new PackageManagerException("Overlay " + pkg.packageName +
-                                    " cannot be upgraded into a static overlay.");
+                        } else if (pkg.isOverlayIsStatic()) {
+                            throw new PackageManagerException("Overlay "
+                                    + pkg.getPackageName()
+                                    + " cannot be upgraded into a static overlay.");
                         }
                     }
                 } else {
                     // The overlay is a non-system overlay. Non-system overlays cannot be static.
-                    if (pkg.mOverlayIsStatic) {
-                        throw new PackageManagerException("Overlay " + pkg.packageName +
-                                " is static but not pre-installed.");
+                    if (pkg.isOverlayIsStatic()) {
+                        throw new PackageManagerException("Overlay "
+                                + pkg.getPackageName()
+                                + " is static but not pre-installed.");
                     }
 
                     // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
                     // signed with the platform certificate. Check this in increasing order of
                     // computational cost.
-                    if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
+                    if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
                         final PackageSetting platformPkgSetting =
                                 mSettings.getPackageLPr("android");
                         if ((platformPkgSetting.signatures.mSigningDetails
                                     != PackageParser.SigningDetails.UNKNOWN)
                                 && (compareSignatures(
                                         platformPkgSetting.signatures.mSigningDetails.signatures,
-                                        pkg.mSigningDetails.signatures)
+                                pkg.getSigningDetails().signatures)
                                     != PackageManager.SIGNATURE_MATCH)) {
-                            throw new PackageManagerException("Overlay " + pkg.packageName
+                            throw new PackageManagerException("Overlay "
+                                    + pkg.getPackageName()
                                     + " must target Q or later, "
                                     + "or be signed with the platform certificate");
                         }
@@ -11511,18 +11392,19 @@
                     // only be used if it is signed with the same certificate as its target. If the
                     // target is already installed, check this here to augment the last line of
                     // defence which is OMS.
-                    if (pkg.mOverlayTargetName == null) {
+                    if (pkg.getOverlayTargetName() == null) {
                         final PackageSetting targetPkgSetting =
-                                mSettings.getPackageLPr(pkg.mOverlayTarget);
+                                mSettings.getPackageLPr(pkg.getOverlayTarget());
                         if (targetPkgSetting != null) {
                             if ((targetPkgSetting.signatures.mSigningDetails
                                         != PackageParser.SigningDetails.UNKNOWN)
                                     && (compareSignatures(
                                             targetPkgSetting.signatures.mSigningDetails.signatures,
-                                            pkg.mSigningDetails.signatures)
+                                    pkg.getSigningDetails().signatures)
                                         != PackageManager.SIGNATURE_MATCH)) {
-                                throw new PackageManagerException("Overlay " + pkg.packageName
-                                        + " and target " + pkg.mOverlayTarget + " signed with"
+                                throw new PackageManagerException("Overlay "
+                                        + pkg.getPackageName() + " and target "
+                                        + pkg.getOverlayTarget() + " signed with"
                                         + " different certificates, and the overlay lacks"
                                         + " <overlay android:targetName>");
                             }
@@ -11602,71 +11484,67 @@
      * Adds a scanned package to the system. When this method is finished, the package will
      * be available for query, resolution, etc...
      */
-    private void commitPackageSettings(PackageParser.Package pkg,
-            @Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting,
+    private void commitPackageSettings(AndroidPackage pkg,
+            @Nullable AndroidPackage oldPkg, PackageSetting pkgSetting,
             final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {
-        final String pkgName = pkg.packageName;
+        final String pkgName = pkg.getPackageName();
         if (mCustomResolverComponentName != null &&
-                mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
+                mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
             setUpCustomResolverActivity(pkg);
         }
 
-        if (pkg.packageName.equals("android")) {
+        if (pkg.getPackageName().equals("android")) {
             synchronized (mLock) {
-                if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
-                    // Set up information for our fall-back user intent resolution activity.
-                    mPlatformPackage = pkg;
-                    pkg.mVersionCode = mSdkVersion;
-                    pkg.mVersionCodeMajor = 0;
-                    mAndroidApplication = pkg.applicationInfo;
-                    if (!mResolverReplaced) {
-                        mResolveActivity.applicationInfo = mAndroidApplication;
-                        mResolveActivity.name = ResolverActivity.class.getName();
-                        mResolveActivity.packageName = mAndroidApplication.packageName;
-                        mResolveActivity.processName = "system:ui";
-                        mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
-                        mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
-                        mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
-                        mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
-                        mResolveActivity.exported = true;
-                        mResolveActivity.enabled = true;
-                        mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-                        mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
-                                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
-                                | ActivityInfo.CONFIG_SCREEN_LAYOUT
-                                | ActivityInfo.CONFIG_ORIENTATION
-                                | ActivityInfo.CONFIG_KEYBOARD
-                                | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
-                        mResolveInfo.activityInfo = mResolveActivity;
-                        mResolveInfo.priority = 0;
-                        mResolveInfo.preferredOrder = 0;
-                        mResolveInfo.match = 0;
-                        mResolveComponentName = new ComponentName(
-                                mAndroidApplication.packageName, mResolveActivity.name);
-                    }
+                // Set up information for our fall-back user intent resolution activity.
+                mPlatformPackage = pkg;
+                mAndroidApplication = pkg.toAppInfo();
+                if (!mResolverReplaced) {
+                    mResolveActivity.applicationInfo = mAndroidApplication;
+                    mResolveActivity.name = ResolverActivity.class.getName();
+                    mResolveActivity.packageName = mAndroidApplication.packageName;
+                    mResolveActivity.processName = "system:ui";
+                    mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+                    mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
+                    mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+                    mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
+                    mResolveActivity.exported = true;
+                    mResolveActivity.enabled = true;
+                    mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+                    mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
+                            | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
+                            | ActivityInfo.CONFIG_SCREEN_LAYOUT
+                            | ActivityInfo.CONFIG_ORIENTATION
+                            | ActivityInfo.CONFIG_KEYBOARD
+                            | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+                    mResolveInfo.activityInfo = mResolveActivity;
+                    mResolveInfo.priority = 0;
+                    mResolveInfo.preferredOrder = 0;
+                    mResolveInfo.match = 0;
+                    mResolveComponentName = new ComponentName(
+                            mAndroidApplication.packageName, mResolveActivity.name);
                 }
             }
         }
 
-        ArrayList<PackageParser.Package> clientLibPkgs = null;
+        ArrayList<AndroidPackage> clientLibPkgs = null;
         // writer
         synchronized (mLock) {
             if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
                 for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
                     commitSharedLibraryInfoLocked(info);
                 }
-                final Map<String, PackageParser.Package> combinedPackages =
-                        reconciledPkg.getCombinedPackages();
+                final Map<String, AndroidPackage> combinedSigningDetails =
+                        reconciledPkg.getCombinedAvailablePackages();
                 try {
                     // Shared libraries for the package need to be updated.
-                    updateSharedLibrariesLocked(pkg, null, combinedPackages);
+                    updateSharedLibrariesLocked(pkg, null, combinedSigningDetails);
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
                 }
                 // Update all applications that use this library. Skip when booting
                 // since this will be done after all packages are scaned.
                 if ((scanFlags & SCAN_BOOTING) == 0) {
-                    clientLibPkgs = updateAllSharedLibrariesLocked(pkg, combinedPackages);
+                    clientLibPkgs = updateAllSharedLibrariesLocked(pkg, combinedSigningDetails);
                 }
             }
         }
@@ -11690,9 +11568,9 @@
         // Also need to kill any apps that are dependent on the library.
         if (clientLibPkgs != null) {
             for (int i=0; i<clientLibPkgs.size(); i++) {
-                PackageParser.Package clientPkg = clientLibPkgs.get(i);
-                killApplication(clientPkg.applicationInfo.packageName,
-                        clientPkg.applicationInfo.uid, "update lib");
+                AndroidPackage clientPkg = clientLibPkgs.get(i);
+                killApplication(clientPkg.getAppInfoPackageName(),
+                        clientPkg.getUid(), "update lib");
             }
         }
 
@@ -11705,7 +11583,7 @@
             // Add the new setting to mSettings
             mSettings.insertPackageSettingLPw(pkgSetting, pkg);
             // Add the new setting to mPackages
-            mPackages.put(pkg.applicationInfo.packageName, pkg);
+            mPackages.put(pkg.getAppInfoPackageName(), pkg);
 
             // Add the package's KeySets to the global KeySetManagerService
             KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -11716,7 +11594,7 @@
 
             // Don't allow ephemeral applications to define new permissions groups.
             if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                Slog.w(TAG, "Permission groups from package " + pkg.packageName
+                Slog.w(TAG, "Permission groups from package " + pkg.getPackageName()
                         + " ignored: instant apps cannot define new permission groups.");
             } else {
                 mPermissionManager.addAllPermissionGroups(pkg, chatty);
@@ -11724,31 +11602,31 @@
 
             // Don't allow ephemeral applications to define new permissions.
             if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                Slog.w(TAG, "Permissions from package " + pkg.packageName
+                Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
                         + " ignored: instant apps cannot define new permissions.");
             } else {
                 mPermissionManager.addAllPermissions(pkg, chatty);
             }
 
-            int collectionSize = pkg.instrumentation.size();
+            int collectionSize = ArrayUtils.size(pkg.getInstrumentations());
             StringBuilder r = null;
             int i;
             for (i = 0; i < collectionSize; i++) {
-                PackageParser.Instrumentation a = pkg.instrumentation.get(i);
-                a.info.packageName = pkg.applicationInfo.packageName;
-                a.info.sourceDir = pkg.applicationInfo.sourceDir;
-                a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
-                a.info.splitNames = pkg.splitNames;
-                a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
-                a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
-                a.info.splitDependencies = pkg.applicationInfo.splitDependencies;
-                a.info.dataDir = pkg.applicationInfo.dataDir;
-                a.info.deviceProtectedDataDir = pkg.applicationInfo.deviceProtectedDataDir;
-                a.info.credentialProtectedDataDir = pkg.applicationInfo.credentialProtectedDataDir;
-                a.info.primaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
-                a.info.secondaryCpuAbi = pkg.applicationInfo.secondaryCpuAbi;
-                a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
-                a.info.secondaryNativeLibraryDir = pkg.applicationInfo.secondaryNativeLibraryDir;
+                ParsedInstrumentation a = pkg.getInstrumentations().get(i);
+                a.setPackageName(pkg.getAppInfoPackageName());
+                a.sourceDir = pkg.getBaseCodePath();
+                a.publicSourceDir = pkg.getPublicSourceDir();
+                a.splitNames = pkg.getSplitNames();
+                a.splitSourceDirs = pkg.getSplitCodePaths();
+                a.splitPublicSourceDirs = pkg.getSplitPublicSourceDirs();
+                a.splitDependencies = pkg.getSplitDependencies();
+                a.dataDir = pkg.getDataDir();
+                a.deviceProtectedDataDir = pkg.getDeviceProtectedDataDir();
+                a.credentialProtectedDataDir = pkg.getCredentialProtectedDataDir();
+                a.primaryCpuAbi = pkg.getPrimaryCpuAbi();
+                a.secondaryCpuAbi = pkg.getSecondaryCpuAbi();
+                a.nativeLibraryDir = pkg.getNativeLibraryDir();
+                a.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
                 mInstrumentation.put(a.getComponentName(), a);
                 if (chatty) {
                     if (r == null) {
@@ -11756,19 +11634,16 @@
                     } else {
                         r.append(' ');
                     }
-                    r.append(a.info.name);
+                    r.append(a.getName());
                 }
             }
             if (r != null) {
                 if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
             }
 
-            if (pkg.protectedBroadcasts != null) {
-                collectionSize = pkg.protectedBroadcasts.size();
+            if (pkg.getProtectedBroadcasts() != null) {
                 synchronized (mProtectedBroadcasts) {
-                    for (i = 0; i < collectionSize; i++) {
-                        mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
-                    }
+                    mProtectedBroadcasts.addAll(pkg.getProtectedBroadcasts());
                 }
             }
 
@@ -11792,14 +11667,14 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    private void setUpCustomResolverActivity(PackageParser.Package pkg) {
+    private void setUpCustomResolverActivity(AndroidPackage pkg) {
         synchronized (mLock) {
             mResolverReplaced = true;
             // Set up information for custom user intent resolution activity.
-            mResolveActivity.applicationInfo = pkg.applicationInfo;
+            mResolveActivity.applicationInfo = pkg.toAppInfo();
             mResolveActivity.name = mCustomResolverComponentName.getClassName();
-            mResolveActivity.packageName = pkg.applicationInfo.packageName;
-            mResolveActivity.processName = pkg.applicationInfo.packageName;
+            mResolveActivity.packageName = pkg.getAppInfoPackageName();
+            mResolveActivity.processName = pkg.getAppInfoProcessName();
             mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
                     ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
@@ -11866,22 +11741,13 @@
         }
     }
 
-    private void removePackageLI(PackageParser.Package pkg, boolean chatty) {
+    private void removePackageLI(AndroidPackage pkg, boolean chatty) {
         // Remove the parent package setting
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        PackageSetting ps = getPackageSetting(pkg.getPackageName());
         if (ps != null) {
             removePackageLI(ps.name, chatty);
         } else if (DEBUG_REMOVE && chatty) {
-            Log.d(TAG, "Not removing package " + pkg.packageName + "; mExtras == null");
-        }
-        // Remove the child package setting
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = pkg.childPackages.get(i);
-            ps = (PackageSetting) childPkg.mExtras;
-            if (ps != null) {
-                removePackageLI(ps.name, chatty);
-            }
+            Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null");
         }
     }
 
@@ -11893,45 +11759,23 @@
 
         // writer
         synchronized (mLock) {
-            final PackageParser.Package removedPackage = mPackages.remove(packageName);
+            final AndroidPackage removedPackage = mPackages.remove(packageName);
             if (removedPackage != null) {
                 cleanPackageDataStructuresLILPw(removedPackage, chatty);
             }
         }
     }
 
-    void removeInstalledPackageLI(PackageParser.Package pkg, boolean chatty) {
-        if (DEBUG_INSTALL) {
-            if (chatty)
-                Log.d(TAG, "Removing package " + pkg.applicationInfo.packageName);
-        }
-
-        // writer
-        synchronized (mLock) {
-            // Remove the parent package
-            mPackages.remove(pkg.applicationInfo.packageName);
-            cleanPackageDataStructuresLILPw(pkg, chatty);
-
-            // Remove the child packages
-            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageParser.Package childPkg = pkg.childPackages.get(i);
-                mPackages.remove(childPkg.applicationInfo.packageName);
-                cleanPackageDataStructuresLILPw(childPkg, chatty);
-            }
-        }
-    }
-
-    void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
+    void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
         mComponentResolver.removeAllComponents(pkg, chatty);
-        mAppsFilter.removePackage(pkg.packageName);
+        mAppsFilter.removePackage(pkg.getPackageName());
         mPermissionManager.removeAllPermissions(pkg, chatty);
 
-        final int instrumentationSize = pkg.instrumentation.size();
+        final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations());
         StringBuilder r = null;
         int i;
         for (i = 0; i < instrumentationSize; i++) {
-            PackageParser.Instrumentation a = pkg.instrumentation.get(i);
+            ParsedInstrumentation a = pkg.getInstrumentations().get(i);
             mInstrumentation.remove(a.getComponentName());
             if (DEBUG_REMOVE && chatty) {
                 if (r == null) {
@@ -11939,7 +11783,7 @@
                 } else {
                     r.append(' ');
                 }
-                r.append(a.info.name);
+                r.append(a.getName());
             }
         }
         if (r != null) {
@@ -11947,12 +11791,12 @@
         }
 
         r = null;
-        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+        if ((pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
             // Only system apps can hold shared libraries.
-            if (pkg.libraryNames != null) {
-                final int libraryNamesSize = pkg.libraryNames.size();
+            if (pkg.getLibraryNames() != null) {
+                final int libraryNamesSize = pkg.getLibraryNames().size();
                 for (i = 0; i < libraryNamesSize; i++) {
-                    String name = pkg.libraryNames.get(i);
+                    String name = pkg.getLibraryNames().get(i);
                     if (removeSharedLibraryLPw(name, 0)) {
                         if (DEBUG_REMOVE && chatty) {
                             if (r == null) {
@@ -11970,15 +11814,16 @@
         r = null;
 
         // Any package can hold static shared libraries.
-        if (pkg.staticSharedLibName != null) {
-            if (removeSharedLibraryLPw(pkg.staticSharedLibName, pkg.staticSharedLibVersion)) {
+        if (pkg.getStaticSharedLibName() != null) {
+            if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
+                    pkg.getStaticSharedLibVersion())) {
                 if (DEBUG_REMOVE && chatty) {
                     if (r == null) {
                         r = new StringBuilder(256);
                     } else {
                         r.append(' ');
                     }
-                    r.append(pkg.staticSharedLibName);
+                    r.append(pkg.getStaticSharedLibName());
                 }
             }
         }
@@ -12319,11 +12164,11 @@
                 // Cannot hide static shared libs as they are considered
                 // a part of the using app (emulating static linking). Also
                 // static libs are installed always on internal storage.
-                PackageParser.Package pkg = mPackages.get(packageName);
-                if (pkg != null && pkg.staticSharedLibName != null) {
+                AndroidPackage pkg = mPackages.get(packageName);
+                if (pkg != null && pkg.getStaticSharedLibName() != null) {
                     Slog.w(TAG, "Cannot hide package: " + packageName
                             + " providing static shared library: "
-                            + pkg.staticSharedLibName);
+                            + pkg.getStaticSharedLibName());
                     return false;
                 }
                 // Only allow protected packages to hide themselves.
@@ -12369,17 +12214,17 @@
             if (pkgSetting == null || !pkgSetting.isSystem()) {
                 return;
             }
-            PackageParser.Package pkg = pkgSetting.pkg;
-            if (pkg != null && pkg.applicationInfo != null) {
-                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            AndroidPackage pkg = pkgSetting.pkg;
+            if (pkg != null) {
+                pkg.mutate().setHiddenUntilInstalled(hidden);
             }
             final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
             if (disabledPs == null) {
                 return;
             }
             pkg = disabledPs.pkg;
-            if (pkg != null && pkg.applicationInfo != null) {
-                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            if (pkg != null) {
+                pkg.mutate().setHiddenUntilInstalled(hidden);
             }
         }
     }
@@ -12575,7 +12420,7 @@
             if (installed) {
                 if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
                         != 0 && pkgSetting.pkg != null) {
-                    whiteListedPermissions = pkgSetting.pkg.requestedPermissions;
+                    whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions();
                 }
                 mPermissionManager.setWhitelistedRestrictedPermissions(packageName,
                         whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
@@ -13012,11 +12857,11 @@
                     // Cannot suspend static shared libs as they are considered
                     // a part of the using app (emulating static linking). Also
                     // static libs are installed always on internal storage.
-                    PackageParser.Package pkg = mPackages.get(packageName);
-                    if (pkg != null && pkg.applicationInfo.isStaticSharedLibrary()) {
+                    AndroidPackage pkg = mPackages.get(packageName);
+                    if (pkg != null && pkg.isStaticSharedLibrary()) {
                         Slog.w(TAG, "Cannot suspend package: " + packageName
                                 + " providing static shared library: "
-                                + pkg.staticSharedLibName);
+                                + pkg.getStaticSharedLibName());
                         continue;
                     }
                 }
@@ -13161,10 +13006,10 @@
 
     private int getUidForVerifier(VerifierInfo verifierInfo) {
         synchronized (mLock) {
-            final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
+            final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
             if (pkg == null) {
                 return -1;
-            } else if (pkg.mSigningDetails.signatures.length != 1) {
+            } else if (pkg.getSigningDetails().signatures.length != 1) {
                 Slog.i(TAG, "Verifier package " + verifierInfo.packageName
                         + " has more than one signature; ignoring");
                 return -1;
@@ -13178,7 +13023,7 @@
 
             final byte[] expectedPublicKey;
             try {
-                final Signature verifierSig = pkg.mSigningDetails.signatures[0];
+                final Signature verifierSig = pkg.getSigningDetails().signatures[0];
                 final PublicKey publicKey = verifierSig.getPublicKey();
                 expectedPublicKey = publicKey.getEncoded();
             } catch (CertificateException e) {
@@ -13193,7 +13038,7 @@
                 return -1;
             }
 
-            return pkg.applicationInfo.uid;
+            return pkg.getUid();
         }
     }
 
@@ -13381,26 +13226,33 @@
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mLock) {
-            PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg == null || pkg.activities == null) {
+            AndroidPackage pkg = mPackages.get(packageName);
+            if (pkg == null || ArrayUtils.isEmpty(pkg.getActivities())) {
                 return ParceledListSlice.emptyList();
             }
-            if (pkg.mExtras == null) {
+            final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+            if (ps == null) {
                 return ParceledListSlice.emptyList();
             }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
             if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 return ParceledListSlice.emptyList();
             }
-            final int count = pkg.activities.size();
+            final int count = ArrayUtils.size(pkg.getActivities());
             ArrayList<IntentFilter> result = new ArrayList<>();
             for (int n=0; n<count; n++) {
-                PackageParser.Activity activity = pkg.activities.get(n);
+                ParsedActivity activity = pkg.getActivities().get(n);
                 if (activity.intents != null && activity.intents.size() > 0) {
                     result.addAll(activity.intents);
                 }
             }
-            return new ParceledListSlice<>(result);
+            return new ParceledListSlice<IntentFilter>(result) {
+                @Override
+                protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
+                    // IntentFilter has final Parcelable methods, so redirect to the subclass
+                    ((ParsedActivityIntentInfo) parcelable).writeIntentInfoToParcel(dest,
+                            callFlags);
+                }
+            };
         }
     }
 
@@ -13581,7 +13433,7 @@
         // package has not opted out of backup participation.
         final boolean update = res.removedInfo != null
                 && res.removedInfo.removedPackage != null;
-        final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
+        final int flags = (res.pkg == null) ? 0 : res.pkg.getFlags();
         boolean doRestore = !update
                 && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
 
@@ -13619,7 +13471,7 @@
                 try {
                     if (bm.isBackupServiceActive(userId)) {
                         bm.restoreAtInstallForUser(
-                                userId, res.pkg.applicationInfo.packageName, token);
+                                userId, res.pkg.getAppInfoPackageName(), token);
                     } else {
                         doRestore = false;
                     }
@@ -13644,8 +13496,8 @@
             IRollbackManager rm = IRollbackManager.Stub.asInterface(
                     ServiceManager.getService(Context.ROLLBACK_SERVICE));
 
-            final String packageName = res.pkg.applicationInfo.packageName;
-            final String seInfo = res.pkg.applicationInfo.seInfo;
+            final String packageName = res.pkg.getAppInfoPackageName();
+            final String seInfo = res.pkg.getSeInfo();
             final int[] allUsers = mUserManager.getUserIds();
             final int[] installedUsers;
 
@@ -13714,7 +13566,7 @@
                 if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                     continue;
                 }
-                if (packageName.equals(data.res.pkg.applicationInfo.packageName)) {
+                if (packageName.equals(data.res.pkg.getAppInfoPackageName())) {
                     // right package; but is it for the right user?
                     for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
                         if (userId == data.res.newUsers[uIndex]) {
@@ -14056,12 +13908,12 @@
             synchronized (mLock) {
                 // Currently installed package which the new package is attempting to replace or
                 // null if no such package is installed.
-                PackageParser.Package installedPkg = mPackages.get(packageName);
+                AndroidPackage installedPkg = mPackages.get(packageName);
                 // Package which currently owns the data which the new package will own if installed.
                 // If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg
                 // will be null whereas dataOwnerPkg will contain information about the package
                 // which was uninstalled while keeping its data.
-                PackageParser.Package dataOwnerPkg = installedPkg;
+                AndroidPackage dataOwnerPkg = installedPkg;
                 if (dataOwnerPkg  == null) {
                     PackageSetting ps = mSettings.mPackages.get(packageName);
                     if (ps != null) {
@@ -14088,7 +13940,7 @@
 
                 if (dataOwnerPkg != null) {
                     if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
-                            dataOwnerPkg.applicationInfo.flags)) {
+                            dataOwnerPkg.getFlags())) {
                         try {
                             checkDowngrade(dataOwnerPkg, pkgLite);
                         } catch (PackageManagerException e) {
@@ -14101,7 +13953,7 @@
                 if (installedPkg != null) {
                     if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                         // Check for updated system application.
-                        if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        if ((installedPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
                             return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                         } else {
                             // If current upgrade specifies particular preference
@@ -14564,7 +14416,7 @@
          * Rename package into final resting place. All paths on the given
          * scanned package should be updated to reflect the rename.
          */
-        abstract boolean doRename(int status, PackageParser.Package pkg);
+        abstract boolean doRename(int status, ParsedPackage parsedPackage);
         abstract int doPostInstall(int status, int uid);
 
         /** @see PackageSettingBase#codePathString */
@@ -14712,7 +14564,8 @@
             return status;
         }
 
-        boolean doRename(int status, PackageParser.Package pkg) {
+        @Override
+        boolean doRename(int status, ParsedPackage parsedPackage) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
                 return false;
@@ -14720,7 +14573,7 @@
 
             final File targetDir = codeFile.getParentFile();
             final File beforeCodeFile = codeFile;
-            final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
+            final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
 
             if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
             try {
@@ -14741,24 +14594,23 @@
 
             // Reflect the rename in scanned details
             try {
-                pkg.setCodePath(afterCodeFile.getCanonicalPath());
+                parsedPackage.setCodePath(afterCodeFile.getCanonicalPath());
             } catch (IOException e) {
                 Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
                 return false;
             }
-            pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
-                    afterCodeFile, pkg.baseCodePath));
-            pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
-                    afterCodeFile, pkg.splitCodePaths));
+            parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, parsedPackage.getBaseCodePath()));
+            parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+                    afterCodeFile, parsedPackage.getSplitCodePaths()));
 
             // Reflect the rename in app info
-            pkg.setApplicationVolumeUuid(pkg.volumeUuid);
-            pkg.setApplicationInfoCodePath(pkg.codePath);
-            pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
-            pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
-            pkg.setApplicationInfoResourcePath(pkg.codePath);
-            pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
-            pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
+            // TODO(b/135203078): Remove all of these application info calls
+            parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
+                    .setApplicationInfoCodePath(parsedPackage.getCodePath())
+                    .setApplicationInfoResourcePath(parsedPackage.getCodePath())
+                    .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
+                    .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
 
             return true;
         }
@@ -14861,20 +14713,20 @@
             return status;
         }
 
-        boolean doRename(int status, PackageParser.Package pkg) {
+        @Override
+        boolean doRename(int status, ParsedPackage parsedPackage) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp(move.toUuid);
                 return false;
             }
 
             // Reflect the move in app info
-            pkg.setApplicationVolumeUuid(pkg.volumeUuid);
-            pkg.setApplicationInfoCodePath(pkg.codePath);
-            pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
-            pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
-            pkg.setApplicationInfoResourcePath(pkg.codePath);
-            pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
-            pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
+            // TODO(b/135203078): Remove all of these application info calls
+            parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
+                    .setApplicationInfoCodePath(parsedPackage.getCodePath())
+                    .setApplicationInfoResourcePath(parsedPackage.getCodePath())
+                    .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
+                    .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
 
             return true;
         }
@@ -14950,14 +14802,14 @@
         int[] origUsers;
         // The set of users that now have this package installed.
         int[] newUsers;
-        PackageParser.Package pkg;
+        AndroidPackage pkg;
         int returnCode;
         String returnMsg;
         String installerPackageName;
         PackageRemovedInfo removedInfo;
         ArrayMap<String, PackageInstalledInfo> addedChildPackages;
         // The set of packages consuming this shared library or null if no consumers exist.
-        ArrayList<PackageParser.Package> libraryConsumers;
+        ArrayList<AndroidPackage> libraryConsumers;
 
         public void setError(int code, String msg) {
             setReturnCode(code);
@@ -15013,123 +14865,39 @@
         }
     }
 
-    /**
-     * Checks whether the parent or any of the child packages have a change shared
-     * user. For a package to be a valid update the shred users of the parent and
-     * the children should match. We may later support changing child shared users.
-     * @param oldPkg The updated package.
-     * @param newPkg The update package.
-     * @return The shared user that change between the versions.
-     */
-    private String getParentOrChildPackageChangedSharedUser(PackageParser.Package oldPkg,
-            PackageParser.Package newPkg) {
-        // Check parent shared user
-        if (!Objects.equals(oldPkg.mSharedUserId, newPkg.mSharedUserId)) {
-            return newPkg.packageName;
-        }
-        // Check child shared users
-        final int oldChildCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
-        final int newChildCount = (newPkg.childPackages != null) ? newPkg.childPackages.size() : 0;
-        for (int i = 0; i < newChildCount; i++) {
-            PackageParser.Package newChildPkg = newPkg.childPackages.get(i);
-            // If this child was present, did it have the same shared user?
-            for (int j = 0; j < oldChildCount; j++) {
-                PackageParser.Package oldChildPkg = oldPkg.childPackages.get(j);
-                if (newChildPkg.packageName.equals(oldChildPkg.packageName)
-                        && !Objects.equals(newChildPkg.mSharedUserId, oldChildPkg.mSharedUserId)) {
-                    return newChildPkg.packageName;
-                }
-            }
-        }
-        return null;
-    }
-
     private void removeNativeBinariesLI(PackageSetting ps) {
-        // Remove the lib path for the parent package
         if (ps != null) {
             NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
-            // Remove the lib path for the child packages
-            final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageSetting childPs = null;
-                synchronized (mLock) {
-                    childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
-                }
-                if (childPs != null) {
-                    NativeLibraryHelper.removeNativeBinariesLI(childPs
-                            .legacyNativeLibraryPathString);
-                }
-            }
         }
     }
 
     @GuardedBy("mLock")
-    private void enableSystemPackageLPw(PackageParser.Package pkg) {
-        // Enable the parent package
-        mSettings.enableSystemPackageLPw(pkg.packageName);
-        // Enable the child packages
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = pkg.childPackages.get(i);
-            mSettings.enableSystemPackageLPw(childPkg.packageName);
-        }
+    private void enableSystemPackageLPw(AndroidPackage pkg) {
+        mSettings.enableSystemPackageLPw(pkg.getPackageName());
     }
 
     @GuardedBy("mLock")
-    private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
-            PackageParser.Package newPkg) {
-        // Disable the parent package (parent always replaced)
-        boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);
-        // Disable the child packages
-        final int childCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = oldPkg.childPackages.get(i);
-            final boolean replace = newPkg.hasChildPackage(childPkg.packageName);
-            disabled |= mSettings.disableSystemPackageLPw(childPkg.packageName, replace);
-        }
-        return disabled;
+    private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
+        return mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
     }
 
-    @GuardedBy("mLock")
-    private void setInstallerPackageNameLPw(PackageParser.Package pkg,
-            String installerPackageName) {
-        // Enable the parent package
-        mSettings.setInstallerPackageName(pkg.packageName, installerPackageName);
-        // Enable the child packages
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = pkg.childPackages.get(i);
-            mSettings.setInstallerPackageName(childPkg.packageName, installerPackageName);
-        }
-    }
-
-    private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
+    private void updateSettingsLI(AndroidPackage newPackage, String installerPackageName,
             int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
-        // Update the parent package setting
         updateSettingsInternalLI(newPackage, installerPackageName, allUsers, res.origUsers,
                 res, user, installReason);
-        // Update the child packages setting
-        final int childCount = (newPackage.childPackages != null)
-                ? newPackage.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPackage = newPackage.childPackages.get(i);
-            PackageInstalledInfo childRes = res.addedChildPackages.get(childPackage.packageName);
-            updateSettingsInternalLI(childPackage, installerPackageName, allUsers,
-                    childRes.origUsers, childRes, user, installReason);
-        }
     }
 
-    private void updateSettingsInternalLI(PackageParser.Package pkg,
+    private void updateSettingsInternalLI(AndroidPackage pkg,
             String installerPackageName, int[] allUsers, int[] installedForUsers,
             PackageInstalledInfo res, UserHandle user, int installReason) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
 
-        final String pkgName = pkg.packageName;
+        final String pkgName = pkg.getPackageName();
 
-        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
+        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
         synchronized (mLock) {
 // NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
-            mPermissionManager.updatePermissions(pkg.packageName, pkg);
+            mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
             // For system-bundled packages, we assume that installing an upgraded version
             // of the package implies that the user actually wants to run that new code,
             // so we enable the package.
@@ -15196,7 +14964,7 @@
                 mSettings.writeKernelMappingLPr(ps);
             }
             res.name = pkgName;
-            res.uid = pkg.applicationInfo.uid;
+            res.uid = pkg.getUid();
             res.pkg = pkg;
             mSettings.setInstallerPackageName(pkgName, installerPackageName);
             res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
@@ -15254,7 +15022,7 @@
     private static class ReconcileRequest {
         public final Map<String, ScanResult> scannedPackages;
 
-        public final Map<String, PackageParser.Package> allPackages;
+        public final Map<String, AndroidPackage> allPackages;
         public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
         public final Map<String, InstallArgs> installArgs;
         public final Map<String, PackageInstalledInfo> installResults;
@@ -15267,7 +15035,7 @@
                 Map<String, PackageInstalledInfo> installResults,
                 Map<String, PrepareResult> preparedPackages,
                 Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
-                Map<String, PackageParser.Package> allPackages,
+                Map<String, AndroidPackage> allPackages,
                 Map<String, VersionInfo> versionInfos,
                 Map<String, PackageSetting> lastStaticSharedLibSettings) {
             this.scannedPackages = scannedPackages;
@@ -15282,7 +15050,7 @@
 
         private ReconcileRequest(Map<String, ScanResult> scannedPackages,
                 Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
-                Map<String, PackageParser.Package> allPackages,
+                Map<String, AndroidPackage> allPackages,
                 Map<String, VersionInfo> versionInfos,
                 Map<String, PackageSetting> lastStaticSharedLibSettings) {
             this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
@@ -15350,15 +15118,17 @@
          * with the package(s) currently being installed. The to-be installed packages take
          * precedence and may shadow already installed packages.
          */
-        private Map<String, PackageParser.Package> getCombinedPackages() {
-            final ArrayMap<String, PackageParser.Package> combinedPackages =
+        private Map<String, AndroidPackage> getCombinedAvailablePackages() {
+            final ArrayMap<String, AndroidPackage> combined =
                     new ArrayMap<>(request.allPackages.size() + request.scannedPackages.size());
 
-            combinedPackages.putAll(request.allPackages);
+            combined.putAll(request.allPackages);
+
             for (ScanResult scanResult : request.scannedPackages.values()) {
-                combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
+                combined.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
             }
-            return combinedPackages;
+
+            return combined;
         }
     }
 
@@ -15371,8 +15141,9 @@
         final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
 
         // make a copy of the existing set of packages so we can combine them with incoming packages
-        final ArrayMap<String, PackageParser.Package> combinedPackages =
+        final ArrayMap<String, AndroidPackage> combinedPackages =
                 new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
+
         combinedPackages.putAll(request.allPackages);
 
         final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
@@ -15382,7 +15153,7 @@
             final ScanResult scanResult = scannedPackages.get(installPackageName);
 
             // add / replace existing with incoming packages
-            combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.pkg);
+            combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
 
             // in the first pass, we'll build up the set of incoming shared libraries
             final List<SharedLibraryInfo> allowedSharedLibInfos =
@@ -15415,7 +15186,7 @@
                         | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
                 deletePackageAction = mayDeletePackageLocked(res.removedInfo,
                         prepareResult.originalPs, prepareResult.disabledPs,
-                        prepareResult.childPackageSettings, deleteFlags, null /* all users */);
+                        deleteFlags, null /* all users */);
                 if (deletePackageAction == null) {
                     throw new ReconcileFailure(
                             PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
@@ -15427,7 +15198,7 @@
 
             final int scanFlags = scanResult.request.scanFlags;
             final int parseFlags = scanResult.request.parseFlags;
-            final PackageParser.Package pkg = scanResult.request.pkg;
+            final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
 
             final PackageSetting disabledPkgSetting = scanResult.request.disabledPkgSetting;
             final PackageSetting lastStaticSharedLibSetting =
@@ -15440,35 +15211,37 @@
             boolean sharedUserSignaturesChanged = false;
             SigningDetails signingDetails = null;
             if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
-                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                 } else {
                     if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                                "Package " + pkg.packageName + " upgrade keys do not match the "
-                                        + "previously installed version");
+                                "Package " + parsedPackage.getPackageName()
+                                        + " upgrade keys do not match the previously installed"
+                                        + " version");
                     } else {
-                        String msg = "System package " + pkg.packageName
+                        String msg = "System package " + parsedPackage.getPackageName()
                                 + " signature changed; retaining data.";
                         reportSettingsProblem(Log.WARN, msg);
                     }
                 }
-                signingDetails = pkg.mSigningDetails;
+                signingDetails = parsedPackage.getSigningDetails();
             } else {
                 try {
                     final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
                     final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
                     final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
                     final boolean compatMatch = verifySignatures(signatureCheckPs,
-                            disabledPkgSetting, pkg.mSigningDetails, compareCompat, compareRecover);
+                            disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
+                            compareRecover);
                     // The new KeySets will be re-added later in the scanning process.
                     if (compatMatch) {
                         removeAppKeySetData = true;
                     }
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
-                    signingDetails = pkg.mSigningDetails;
+                    signingDetails = parsedPackage.getSigningDetails();
 
 
                     // if this is is a sharedUser, check to see if the new package is signed by a
@@ -15476,10 +15249,10 @@
                     // signing certificate than the existing one, and if so, copy over the new
                     // details
                     if (signatureCheckPs.sharedUser != null) {
-                        if (pkg.mSigningDetails.hasAncestor(
+                        if (parsedPackage.getSigningDetails().hasAncestor(
                                 signatureCheckPs.sharedUser.signatures.mSigningDetails)) {
                             signatureCheckPs.sharedUser.signatures.mSigningDetails =
-                                    pkg.mSigningDetails;
+                                    parsedPackage.getSigningDetails();
                         }
                         if (signatureCheckPs.sharedUser.signaturesChanged == null) {
                             signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
@@ -15489,7 +15262,7 @@
                     if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw new ReconcileFailure(e);
                     }
-                    signingDetails = pkg.mSigningDetails;
+                    signingDetails = parsedPackage.getSigningDetails();
 
                     // If the system app is part of a shared user we allow that shared user to
                     // change
@@ -15504,7 +15277,7 @@
                                 signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
                         if (signatureCheckPs.sharedUser.signaturesChanged != null
                                 && compareSignatures(sharedUserSignatures,
-                                        pkg.mSigningDetails.signatures)
+                                parsedPackage.getSigningDetails().signatures)
                                         != PackageManager.SIGNATURE_MATCH) {
                             if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
                                 // Mismatched signatures is an error and silently skipping system
@@ -15524,18 +15297,19 @@
                                 // whichever package happened to be scanned later.
                                 throw new IllegalStateException(
                                         "Signature mismatch on system package "
-                                                + pkg.packageName + " for shared user "
+                                                + parsedPackage.getPackageName()
+                                                + " for shared user "
                                                 + scanResult.pkgSetting.sharedUser);
                             }
                         }
 
                         sharedUserSignaturesChanged = true;
                         signatureCheckPs.sharedUser.signatures.mSigningDetails =
-                                pkg.mSigningDetails;
+                                parsedPackage.getSigningDetails();
                         signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
                     }
                     // File a report about this.
-                    String msg = "System package " + pkg.packageName
+                    String msg = "System package " + parsedPackage.getPackageName()
                             + " signature changed; retaining data.";
                     reportSettingsProblem(Log.WARN, msg);
                 } catch (IllegalArgumentException e) {
@@ -15569,8 +15343,9 @@
             }
             try {
                 result.get(installPackageName).collectedSharedLibraryInfos =
-                        collectSharedLibraryInfos(scanResult.request.pkg, combinedPackages,
-                                request.sharedLibrarySource, incomingSharedLibraries);
+                        collectSharedLibraryInfos(scanResult.request.parsedPackage,
+                                combinedPackages, request.sharedLibrarySource,
+                                incomingSharedLibraries);
 
             } catch (PackageManagerException e) {
                 throw new ReconcileFailure(e.error, e.getMessage());
@@ -15588,7 +15363,7 @@
             ScanResult scanResult,
             Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
         // Let's used the parsed package as scanResult.pkgSetting may be null
-        final PackageParser.Package pkg = scanResult.request.pkg;
+        final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
         if (scanResult.staticSharedLibraryInfo == null
                 && scanResult.dynamicSharedLibraryInfos == null) {
             return null;
@@ -15599,12 +15374,12 @@
             return Collections.singletonList(scanResult.staticSharedLibraryInfo);
         }
         final boolean hasDynamicLibraries =
-                (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+                (parsedPackage.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
                         && scanResult.dynamicSharedLibraryInfos != null;
         if (!hasDynamicLibraries) {
             return null;
         }
-        final boolean isUpdatedSystemApp = pkg.isUpdatedSystemApp();
+        final boolean isUpdatedSystemApp = parsedPackage.isUpdatedSystemApp();
         // We may not yet have disabled the updated package yet, so be sure to grab the
         // current setting if that's the case.
         final PackageSetting updatedSystemPs = isUpdatedSystemApp
@@ -15613,9 +15388,9 @@
                         : scanResult.request.disabledPkgSetting
                 : null;
         if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
-                || updatedSystemPs.pkg.libraryNames == null)) {
-            Slog.w(TAG, "Package " + pkg.packageName + " declares libraries that are not "
-                    + "declared on the system image; skipping");
+                || updatedSystemPs.pkg.getLibraryNames() == null)) {
+            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                    + " declares libraries that are not declared on the system image; skipping");
             return null;
         }
         final ArrayList<SharedLibraryInfo> infos =
@@ -15633,16 +15408,17 @@
                 // with it.  Better to just have the restriction here, be
                 // conservative, and create many fewer cases that can negatively
                 // impact the user experience.
-                if (!updatedSystemPs.pkg.libraryNames.contains(name)) {
-                    Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+                if (!updatedSystemPs.pkg.getLibraryNames().contains(name)) {
+                    Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                            + " declares library " + name
                             + " that is not declared on system image; skipping");
                     continue;
                 }
             }
             if (sharedLibExists(
                     name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
-                Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
-                        + " that already exists; skipping");
+                Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library "
+                        + name + " that already exists; skipping");
                 continue;
             }
             infos.add(info);
@@ -15680,68 +15456,38 @@
         for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
             final ScanResult scanResult = reconciledPkg.scanResult;
             final ScanRequest scanRequest = scanResult.request;
-            final PackageParser.Package pkg = scanRequest.pkg;
-            final String packageName = pkg.packageName;
+            final ParsedPackage parsedPackage = scanRequest.parsedPackage;
+            final String packageName = parsedPackage.getPackageName();
             final PackageInstalledInfo res = reconciledPkg.installResult;
 
             if (reconciledPkg.prepareResult.replace) {
-                PackageParser.Package oldPackage = mPackages.get(packageName);
+                AndroidPackage oldPackage = mPackages.get(packageName);
 
                 // Set the update and install times
-                PackageSetting deletedPkgSetting = (PackageSetting) oldPackage.mExtras;
-                setInstallAndUpdateTime(pkg, deletedPkgSetting.firstInstallTime,
-                        System.currentTimeMillis());
+                PackageSetting deletedPkgSetting = getPackageSetting(oldPackage.getPackageName());
+                reconciledPkg.pkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
+                reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();
 
                 if (reconciledPkg.prepareResult.system) {
                     // Remove existing system package
                     removePackageLI(oldPackage, true);
-                    if (!disableSystemPackageLPw(oldPackage, pkg)) {
+                    if (!disableSystemPackageLPw(oldPackage)) {
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
                         res.removedInfo.args = createInstallArgsForExisting(
-                                oldPackage.applicationInfo.getCodePath(),
-                                oldPackage.applicationInfo.getResourcePath(),
-                                getAppDexInstructionSets(oldPackage.applicationInfo));
+                                oldPackage.getAppInfoCodePath(),
+                                oldPackage.getAppInfoResourcePath(),
+                                getAppDexInstructionSets(oldPackage.getPrimaryCpuAbi(),
+                                        oldPackage.getSecondaryCpuAbi()));
                     } else {
                         res.removedInfo.args = null;
                     }
-
-                    // Update the package dynamic state if succeeded
-                    // Now that the install succeeded make sure we remove data
-                    // directories for any child package the update removed.
-                    final int deletedChildCount = (oldPackage.childPackages != null)
-                            ? oldPackage.childPackages.size() : 0;
-                    final int newChildCount = (pkg.childPackages != null)
-                            ? pkg.childPackages.size() : 0;
-                    for (int i = 0; i < deletedChildCount; i++) {
-                        PackageParser.Package deletedChildPkg = oldPackage.childPackages.get(i);
-                        boolean childPackageDeleted = true;
-                        for (int j = 0; j < newChildCount; j++) {
-                            PackageParser.Package newChildPkg = pkg.childPackages.get(j);
-                            if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
-                                childPackageDeleted = false;
-                                break;
-                            }
-                        }
-                        if (childPackageDeleted) {
-                            PackageSetting ps1 = mSettings.getDisabledSystemPkgLPr(
-                                    deletedChildPkg.packageName);
-                            if (ps1 != null && res.removedInfo.removedChildPackages != null) {
-                                PackageRemovedInfo removedChildRes = res.removedInfo
-                                        .removedChildPackages.get(deletedChildPkg.packageName);
-                                removePackageDataLIF(ps1, request.mAllUsers, removedChildRes, 0,
-                                        false);
-                                removedChildRes.removedForAllUsers = mPackages.get(ps1.name)
-                                        == null;
-                            }
-                        }
-                    }
                 } else {
                     try {
                         // Settings will be written during the call to updateSettingsLI().
                         executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
-                                true, request.mAllUsers, false, pkg);
+                                true, request.mAllUsers, false, parsedPackage);
                     } catch (SystemDeleteException e) {
                         if (Build.IS_ENG) {
                             throw new RuntimeException("Unexpected failure", e);
@@ -15757,62 +15503,40 @@
                             Slog.i(TAG, "upgrading pkg " + oldPackage
                                     + " is ASEC-hosted -> UNAVAILABLE");
                         }
-                        final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+                        final int[] uidArray = new int[]{oldPackage.getUid()};
                         final ArrayList<String> pkgList = new ArrayList<>(1);
-                        pkgList.add(oldPackage.applicationInfo.packageName);
+                        pkgList.add(oldPackage.getAppInfoPackageName());
                         sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
                     }
 
                     // Update the in-memory copy of the previous code paths.
                     PackageSetting ps1 = mSettings.mPackages.get(
-                            reconciledPkg.prepareResult.existingPackage.packageName);
+                            reconciledPkg.prepareResult.existingPackage.getPackageName());
                     if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
                             == 0) {
                         if (ps1.mOldCodePaths == null) {
                             ps1.mOldCodePaths = new ArraySet<>();
                         }
-                        Collections.addAll(ps1.mOldCodePaths, oldPackage.baseCodePath);
-                        if (oldPackage.splitCodePaths != null) {
-                            Collections.addAll(ps1.mOldCodePaths, oldPackage.splitCodePaths);
+                        Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseCodePath());
+                        if (oldPackage.getSplitCodePaths() != null) {
+                            Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
                         }
                     } else {
                         ps1.mOldCodePaths = null;
                     }
-                    if (ps1.childPackageNames != null) {
-                        for (int i = ps1.childPackageNames.size() - 1; i >= 0; --i) {
-                            final String childPkgName = ps1.childPackageNames.get(i);
-                            final PackageSetting childPs = mSettings.mPackages.get(childPkgName);
-                            childPs.mOldCodePaths = ps1.mOldCodePaths;
-                        }
-                    }
 
                     if (reconciledPkg.installResult.returnCode
                             == PackageManager.INSTALL_SUCCEEDED) {
-                        PackageSetting ps2 = mSettings.getPackageLPr(pkg.packageName);
+                        PackageSetting ps2 = mSettings.getPackageLPr(
+                                parsedPackage.getPackageName());
                         if (ps2 != null) {
                             res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
-                            if (res.removedInfo.removedChildPackages != null) {
-                                final int childCount1 = res.removedInfo.removedChildPackages.size();
-                                // Iterate in reverse as we may modify the collection
-                                for (int i = childCount1 - 1; i >= 0; i--) {
-                                    String childPackageName =
-                                            res.removedInfo.removedChildPackages.keyAt(i);
-                                    if (res.addedChildPackages.containsKey(childPackageName)) {
-                                        res.removedInfo.removedChildPackages.removeAt(i);
-                                    } else {
-                                        PackageRemovedInfo childInfo = res.removedInfo
-                                                .removedChildPackages.valueAt(i);
-                                        childInfo.removedForAllUsers = mPackages.get(
-                                                childInfo.removedPackage) == null;
-                                    }
-                                }
-                            }
                         }
                     }
                 }
             }
 
-            commitReconciledScanResultLocked(reconciledPkg);
+            AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg);
             updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
                     res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);
 
@@ -15821,17 +15545,6 @@
                 res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
                 ps.setUpdateAvailable(false /*updateAvailable*/);
             }
-            final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageParser.Package childPkg = pkg.childPackages.get(i);
-                PackageInstalledInfo childRes = res.addedChildPackages.get(
-                        childPkg.packageName);
-                PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-                if (childPs != null) {
-                    childRes.newUsers = childPs.queryInstalledUsers(
-                            mUserManager.getUserIds(), true);
-                }
-            }
             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                 updateSequenceNumberLP(ps, res.newUsers);
                 updateInstantAppInstallerLocked(packageName);
@@ -15891,33 +15604,31 @@
                 request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                 request.installResult.installerPackageName = request.args.installerPackageName;
 
-                final String packageName = prepareResult.packageToScan.packageName;
+                final String packageName = prepareResult.packageToScan.getPackageName();
                 prepareResults.put(packageName, prepareResult);
                 installResults.put(packageName, request.installResult);
                 installArgs.put(packageName, request.args);
                 try {
-                    final List<ScanResult> scanResults = scanPackageTracedLI(
+                    final ScanResult result = scanPackageTracedLI(
                             prepareResult.packageToScan, prepareResult.parseFlags,
                             prepareResult.scanFlags, System.currentTimeMillis(),
                             request.args.user);
-                    for (ScanResult result : scanResults) {
-                        if (null != preparedScans.put(result.pkgSetting.pkg.packageName, result)) {
-                            request.installResult.setError(
-                                    PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
-                                    "Duplicate package " + result.pkgSetting.pkg.packageName
-                                            + " in multi-package install request.");
-                            return;
-                        }
-                        createdAppId.put(packageName, optimisticallyRegisterAppId(result));
-                        versionInfos.put(result.pkgSetting.pkg.packageName,
-                                getSettingsVersionForPackage(result.pkgSetting.pkg));
-                        if (result.staticSharedLibraryInfo != null) {
-                            final PackageSetting sharedLibLatestVersionSetting =
-                                    getSharedLibLatestVersionSetting(result);
-                            if (sharedLibLatestVersionSetting != null) {
-                                lastStaticSharedLibSettings.put(result.pkgSetting.pkg.packageName,
-                                        sharedLibLatestVersionSetting);
-                            }
+                    if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
+                        request.installResult.setError(
+                                PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+                                "Duplicate package " + result.pkgSetting.pkg.getPackageName()
+                                        + " in multi-package install request.");
+                        return;
+                    }
+                    createdAppId.put(packageName, optimisticallyRegisterAppId(result));
+                    versionInfos.put(result.pkgSetting.pkg.getPackageName(),
+                            getSettingsVersionForPackage(result.pkgSetting.pkg));
+                    if (result.staticSharedLibraryInfo != null) {
+                        final PackageSetting sharedLibLatestVersionSetting =
+                                getSharedLibLatestVersionSetting(result);
+                        if (sharedLibLatestVersionSetting != null) {
+                            lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),
+                                    sharedLibLatestVersionSetting);
                         }
                     }
                 } catch (PackageManagerException e) {
@@ -15960,7 +15671,8 @@
         } finally {
             if (!success) {
                 for (ScanResult result : preparedScans.values()) {
-                    if (createdAppId.getOrDefault(result.request.pkg.packageName, false)) {
+                    if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
+                            false)) {
                         cleanUpAppIdCreation(result);
                     }
                 }
@@ -15990,16 +15702,16 @@
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
             final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
                             & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
-            final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
-            final String packageName = pkg.packageName;
+            final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
+            final String packageName = pkg.getPackageName();
             prepareAppDataAfterInstallLIF(pkg);
             if (reconciledPkg.prepareResult.clearCodeCache) {
                 clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
                         | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
             }
             if (reconciledPkg.prepareResult.replace) {
-                mDexManager.notifyPackageUpdated(pkg.packageName,
-                        pkg.baseCodePath, pkg.splitCodePaths);
+                mDexManager.notifyPackageUpdated(pkg.getPackageName(),
+                        pkg.getBaseCodePath(), pkg.getSplitCodePaths());
             }
 
             // Prepare the application profiles for the new code paths.
@@ -16013,7 +15725,7 @@
             // Check whether we need to dexopt the app.
             //
             // NOTE: it is IMPORTANT to call dexopt:
-            //   - after doRename which will sync the package data from PackageParser.Package and
+            //   - after doRename which will sync the package data from AndroidPackage and
             //     its corresponding ApplicationInfo.
             //   - after installNewPackageLIF or replacePackageLIF which will update result with the
             //     uid of the application (pkg.applicationInfo.uid).
@@ -16032,7 +15744,7 @@
             final boolean performDexopt =
                     (!instantApp || Global.getInt(mContext.getContentResolver(),
                     Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
-                    && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+                    && ((pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
 
             if (performDexopt) {
                 // Compile the layout resources.
@@ -16080,8 +15792,8 @@
         public final int scanFlags;
         public final int parseFlags;
         @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
-        public final PackageParser.Package existingPackage;
-        public final PackageParser.Package packageToScan;
+        public final AndroidPackage existingPackage;
+        public final ParsedPackage packageToScan;
         public final boolean clearCodeCache;
         public final boolean system;
         /* The original package name if it was changed during an update, otherwise {@code null}. */
@@ -16090,14 +15802,13 @@
         public final PackageFreezer freezer;
         public final PackageSetting originalPs;
         public final PackageSetting disabledPs;
-        public final PackageSetting[] childPackageSettings;
 
         private PrepareResult(int installReason, String volumeUuid,
                 String installerPackageName, UserHandle user, boolean replace, int scanFlags,
-                int parseFlags, PackageParser.Package existingPackage,
-                PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
+                int parseFlags, AndroidPackage existingPackage,
+                ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
                 String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
-                PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
+                PackageSetting disabledPs) {
             this.installReason = installReason;
             this.volumeUuid = volumeUuid;
             this.installerPackageName = installerPackageName;
@@ -16113,7 +15824,6 @@
             this.freezer = freezer;
             this.originalPs = originalPs;
             this.disabledPs = disabledPs;
-            this.childPackageSettings = childPackageSettings;
         }
     }
 
@@ -16194,10 +15904,10 @@
         pp.setCallback(mPackageParserCallback);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
-        final PackageParser.Package pkg;
+        ParsedPackage parsedPackage;
         try {
-            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
-            DexMetadataHelper.validatePackageDexMetadata(pkg);
+            parsedPackage = pp.parseParsedPackage(tmpPackageFile, parseFlags, false);
+            DexMetadataHelper.validatePackageDexMetadata(parsedPackage);
         } catch (PackageParserException e) {
             throw new PrepareFailure("Failed parse during installPackageLI", e);
         } finally {
@@ -16206,23 +15916,23 @@
 
         // Instant apps have several additional install-time checks.
         if (instantApp) {
-            if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
-                Slog.w(TAG,
-                        "Instant app package " + pkg.packageName + " does not target at least O");
+            if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+                Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+                                + " does not target at least O");
                 throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                         "Instant app package must target at least O");
             }
-            if (pkg.mSharedUserId != null) {
-                Slog.w(TAG, "Instant app package " + pkg.packageName
+            if (parsedPackage.getSharedUserId() != null) {
+                Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                         + " may not declare sharedUserId.");
                 throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                         "Instant app package may not declare a sharedUserId");
             }
         }
 
-        if (pkg.applicationInfo.isStaticSharedLibrary()) {
+        if (parsedPackage.isStaticSharedLibrary()) {
             // Static shared libraries have synthetic package names
-            renameStaticSharedLibraryPackage(pkg);
+            renameStaticSharedLibraryPackage(parsedPackage);
 
             // No static shared libs on external storage
             if (onExternal) {
@@ -16232,42 +15942,16 @@
             }
         }
 
-        // If we are installing a clustered package add results for the children
-        if (pkg.childPackages != null) {
-            synchronized (mLock) {
-                final int childCount = pkg.childPackages.size();
-                for (int i = 0; i < childCount; i++) {
-                    PackageParser.Package childPkg = pkg.childPackages.get(i);
-                    PackageInstalledInfo childRes = new PackageInstalledInfo();
-                    childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-                    childRes.pkg = childPkg;
-                    childRes.name = childPkg.packageName;
-                    PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-                    if (childPs != null) {
-                        childRes.origUsers = childPs.queryInstalledUsers(
-                                mUserManager.getUserIds(), true);
-                    }
-                    if ((mPackages.containsKey(childPkg.packageName))) {
-                        childRes.removedInfo = new PackageRemovedInfo(this);
-                        childRes.removedInfo.removedPackage = childPkg.packageName;
-                        childRes.removedInfo.installerPackageName = childPs.installerPackageName;
-                    }
-                    if (res.addedChildPackages == null) {
-                        res.addedChildPackages = new ArrayMap<>();
-                    }
-                    res.addedChildPackages.put(childPkg.packageName, childRes);
-                }
-            }
-        }
-
         // If package doesn't declare API override, mark that we have an install
         // time CPU ABI override.
-        if (TextUtils.isEmpty(pkg.cpuAbiOverride)) {
-            pkg.cpuAbiOverride = args.abiOverride;
+        // TODO(b/135203078): Isn't this always true because cpuAbiOverride isn't assigned during
+        //  parsing?
+        if (TextUtils.isEmpty(parsedPackage.getCpuAbiOverride())) {
+            parsedPackage.setCpuAbiOverride(args.abiOverride);
         }
 
-        String pkgName = res.name = pkg.packageName;
-        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+        String pkgName = res.name = parsedPackage.getPackageName();
+        if ((parsedPackage.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
             if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
                 throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
             }
@@ -16276,17 +15960,17 @@
         try {
             // either use what we've been given or parse directly from the APK
             if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
-                pkg.setSigningDetails(args.signingDetails);
+                parsedPackage.setSigningDetails(args.signingDetails);
             } else {
-                PackageParser.collectCertificates(pkg, false /* skipVerify */);
+                ApkParseUtils.collectCertificates(parsedPackage, false /* skipVerify */);
             }
         } catch (PackageParserException e) {
             throw new PrepareFailure("Failed collect during installPackageLI", e);
         }
 
-        if (instantApp && pkg.mSigningDetails.signatureSchemeVersion
+        if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
                 < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
-            Slog.w(TAG, "Instant app package " + pkg.packageName
+            Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                     + " is not signed with at least APK Signature Scheme v2");
             throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
                     "Instant app package must be signed with APK Signature Scheme v2 or greater");
@@ -16300,15 +15984,15 @@
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 String oldName = mSettings.getRenamedPackageLPr(pkgName);
-                if (pkg.mOriginalPackages != null
-                        && pkg.mOriginalPackages.contains(oldName)
+                if (parsedPackage.getOriginalPackages() != null
+                        && parsedPackage.getOriginalPackages().contains(oldName)
                         && mPackages.containsKey(oldName)) {
                     // This package is derived from an original package,
                     // and this device has been updating from that original
                     // name.  We must continue using the original name, so
                     // rename the new package here.
-                    pkg.setPackageName(oldName);
-                    pkgName = pkg.packageName;
+                    parsedPackage.setPackageName(oldName);
+                    pkgName = parsedPackage.getPackageName();
                     replace = true;
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "Replacing existing renamed package: oldName="
@@ -16321,43 +16005,27 @@
                     if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                 }
 
-                // Child packages are installed through the parent package
-                if (pkg.parentPackage != null) {
-                    throw new PrepareFailure(
-                            PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
-                            "Package " + pkg.packageName + " is child of package "
-                                    + pkg.parentPackage.parentPackage + ". Child packages "
-                                    + "can be updated only through the parent package.");
-                }
-
                 if (replace) {
                     // Prevent apps opting out from runtime permissions
-                    PackageParser.Package oldPackage = mPackages.get(pkgName);
-                    final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
-                    final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
+                    AndroidPackage oldPackage = mPackages.get(pkgName);
+                    final int oldTargetSdk = oldPackage.getTargetSdkVersion();
+                    final int newTargetSdk = parsedPackage.getTargetSdkVersion();
                     if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
                             && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                         throw new PrepareFailure(
                                 PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
-                                "Package " + pkg.packageName + " new target SDK " + newTargetSdk
+                                "Package " + parsedPackage.getPackageName()
+                                        + " new target SDK " + newTargetSdk
                                         + " doesn't support runtime permissions but the old"
                                         + " target SDK " + oldTargetSdk + " does.");
                     }
                     // Prevent persistent apps from being updated
-                    if (((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0)
+                    if (((oldPackage.getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0)
                             && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
                         throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
-                                "Package " + oldPackage.packageName + " is a persistent app. "
+                                "Package " + oldPackage.getPackageName() + " is a persistent app. "
                                         + "Persistent apps are not updateable.");
                     }
-                    // Prevent installing of child packages
-                    if (oldPackage.parentPackage != null) {
-                        throw new PrepareFailure(
-                                PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
-                                "Package " + pkg.packageName + " is child of package "
-                                        + oldPackage.parentPackage + ". Child packages "
-                                        + "can be updated only through the parent package.");
-                    }
                 }
             }
 
@@ -16370,8 +16038,8 @@
                 // of the same package, therefore we need to compare signatures against
                 // the package setting for the latest library version.
                 PackageSetting signatureCheckPs = ps;
-                if (pkg.applicationInfo.isStaticSharedLibrary()) {
-                    SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(pkg);
+                if (parsedPackage.isStaticSharedLibrary()) {
+                    SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(parsedPackage);
                     if (libraryInfo != null) {
                         signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
                     }
@@ -16382,23 +16050,23 @@
                 // bail early here before tripping over redefined permissions.
                 final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                 if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
-                    if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+                    if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
                         throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
-                                + pkg.packageName + " upgrade keys do not match the "
+                                + parsedPackage.getPackageName() + " upgrade keys do not match the "
                                 + "previously installed version");
                     }
                 } else {
                     try {
-                        final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
-                        final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
+                        final boolean compareCompat = isCompatSignatureUpdateNeeded(parsedPackage);
+                        final boolean compareRecover = isRecoverSignatureUpdateNeeded(
+                                parsedPackage);
                         // We don't care about disabledPkgSetting on install for now.
-                        final boolean compatMatch = verifySignatures(
-                                signatureCheckPs, null, pkg.mSigningDetails, compareCompat,
-                                compareRecover);
+                        final boolean compatMatch = verifySignatures(signatureCheckPs, null,
+                                parsedPackage.getSigningDetails(), compareCompat, compareRecover);
                         // The new KeySets will be re-added later in the scanning process.
                         if (compatMatch) {
                             synchronized (mLock) {
-                                ksms.removeAppKeySetDataLPw(pkg.packageName);
+                                ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
                             }
                         }
                     } catch (PackageManagerException e) {
@@ -16406,27 +16074,25 @@
                     }
                 }
 
-                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
-                    systemApp = (ps.pkg.applicationInfo.flags &
-                            ApplicationInfo.FLAG_SYSTEM) != 0;
+                if (ps.pkg != null) {
+                    systemApp = (ps.pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
                 }
                 res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
             }
 
 
-            int N = pkg.permissions.size();
+            int N = ArrayUtils.size(parsedPackage.getPermissions());
             for (int i = N - 1; i >= 0; i--) {
-                final PackageParser.Permission perm = pkg.permissions.get(i);
-                final BasePermission bp =
-                        (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
+                final ParsedPermission perm = parsedPackage.getPermissions().get(i);
+                final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
 
                 // Don't allow anyone but the system to define ephemeral permissions.
-                if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
+                if ((perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
                         && !systemApp) {
-                    Slog.w(TAG, "Non-System package " + pkg.packageName
+                    Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
                             + " attempting to delcare ephemeral permission "
-                            + perm.info.name + "; Removing ephemeral.");
-                    perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
+                            + perm.getName() + "; Removing ephemeral.");
+                    perm.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
                 }
 
                 // Check whether the newly-scanned package wants to define an already-defined perm
@@ -16438,26 +16104,27 @@
                     final String sourcePackageName = bp.getSourcePackageName();
                     final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
                     final KeySetManagerService ksms = mSettings.mKeySetManagerService;
-                    if (sourcePackageName.equals(pkg.packageName)
+                    if (sourcePackageName.equals(parsedPackage.getPackageName())
                             && (ksms.shouldCheckUpgradeKeySetLocked(
                             sourcePackageSetting, scanFlags))) {
-                        sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
+                        sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
                     } else {
 
                         // in the event of signing certificate rotation, we need to see if the
                         // package's certificate has rotated from the current one, or if it is an
                         // older certificate with which the current is ok with sharing permissions
                         if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
-                                pkg.mSigningDetails,
+                                parsedPackage.getSigningDetails(),
                                 PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
                             sigsOk = true;
-                        } else if (pkg.mSigningDetails.checkCapability(
+                        } else if (parsedPackage.getSigningDetails().checkCapability(
                                 sourcePackageSetting.signatures.mSigningDetails,
                                 PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
 
                             // the scanned package checks out, has signing certificate rotation
                             // history, and is newer; bring it over
-                            sourcePackageSetting.signatures.mSigningDetails = pkg.mSigningDetails;
+                            sourcePackageSetting.signatures.mSigningDetails =
+                                    parsedPackage.getSigningDetails();
                             sigsOk = true;
                         } else {
                             sigsOk = false;
@@ -16469,30 +16136,31 @@
                         // redefinitions.
                         if (!sourcePackageName.equals("android")) {
                             throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
-                                    + pkg.packageName
+                                    + parsedPackage.getPackageName()
                                     + " attempting to redeclare permission "
-                                    + perm.info.name + " already owned by "
+                                    + perm.getName() + " already owned by "
                                     + sourcePackageName)
-                                    .conflictsWithExistingPermission(perm.info.name,
+                                    .conflictsWithExistingPermission(perm.getName(),
                                             sourcePackageName);
                         } else {
-                            Slog.w(TAG, "Package " + pkg.packageName
+                            Slog.w(TAG, "Package " + parsedPackage.getPackageName()
                                     + " attempting to redeclare system permission "
-                                    + perm.info.name + "; ignoring new declaration");
-                            pkg.permissions.remove(i);
+                                    + perm.getName() + "; ignoring new declaration");
+                            parsedPackage.removePermission(i);
                         }
-                    } else if (!PLATFORM_PACKAGE_NAME.equals(pkg.packageName)) {
+                    } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
                         // Prevent apps to change protection level to dangerous from any other
                         // type as this would allow a privilege escalation where an app adds a
                         // normal/signature permission in other app's group and later redefines
                         // it as dangerous leading to the group auto-grant.
-                        if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+                        if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                                 == PermissionInfo.PROTECTION_DANGEROUS) {
                             if (bp != null && !bp.isRuntime()) {
-                                Slog.w(TAG, "Package " + pkg.packageName + " trying to change a "
-                                        + "non-runtime permission " + perm.info.name
+                                Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+                                        + " trying to change a non-runtime permission "
+                                        + perm.getName()
                                         + " to runtime; keeping old protection level");
-                                perm.info.protectionLevel = bp.getProtectionLevel();
+                                perm.protectionLevel = bp.getProtectionLevel();
                             }
                         }
                     }
@@ -16526,8 +16194,8 @@
 
                 // We moved the entire application as-is, so bring over the
                 // previously derived ABI information.
-                pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
-                pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
+                parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)
+                        .setSecondaryCpuAbi(ps.secondaryCpuAbiString);
             }
 
         } else {
@@ -16535,14 +16203,14 @@
             scanFlags |= SCAN_NO_DEX;
 
             try {
-                String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
-                        args.abiOverride : pkg.cpuAbiOverride);
-                final boolean extractNativeLibs = !pkg.isLibrary();
+                String abiOverride = (TextUtils.isEmpty(parsedPackage.getCpuAbiOverride())
+                        ? args.abiOverride : parsedPackage.getCpuAbiOverride());
+                final boolean extractNativeLibs = !parsedPackage.isLibrary();
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
                         derivedAbi = mInjector.getAbiHelper().derivePackageAbi(
-                                pkg, abiOverride, extractNativeLibs);
-                derivedAbi.first.applyTo(pkg);
-                derivedAbi.second.applyTo(pkg);
+                                parsedPackage, abiOverride, extractNativeLibs);
+                derivedAbi.first.applyTo(parsedPackage);
+                derivedAbi.second.applyTo(parsedPackage);
             } catch (PackageManagerException pme) {
                 Slog.e(TAG, "Error deriving application ABI", pme);
                 throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
@@ -16550,19 +16218,19 @@
             }
         }
 
-        if (!args.doRename(res.returnCode, pkg)) {
+        if (!args.doRename(res.returnCode, parsedPackage)) {
             throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
         }
 
         try {
-            setUpFsVerityIfPossible(pkg);
+            setUpFsVerityIfPossible(parsedPackage);
         } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
             throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
                     "Failed to set up verity: " + e);
         }
 
         if (!instantApp) {
-            startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+            startIntentFilterVerifications(args.user.getIdentifier(), replace, parsedPackage);
         } else {
             if (DEBUG_DOMAIN_VERIFICATION) {
                 Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
@@ -16572,7 +16240,7 @@
                 freezePackageForInstall(pkgName, installFlags, "installPackageLI");
         boolean shouldCloseFreezerBeforeReturn = true;
         try {
-            final PackageParser.Package existingPackage;
+            final AndroidPackage existingPackage;
             String renamedPackage = null;
             boolean sysPkg = false;
             String targetVolumeUuid = volumeUuid;
@@ -16583,14 +16251,15 @@
             final PackageSetting[] childPackages;
             if (replace) {
                 targetVolumeUuid = null;
-                if (pkg.applicationInfo.isStaticSharedLibrary()) {
+                if (parsedPackage.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
                     // unless this is the exact same version code which is useful for
                     // development.
-                    PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
+                    AndroidPackage existingPkg = mPackages.get(parsedPackage.getPackageName());
                     if (existingPkg != null
-                            && existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
+                            && existingPkg.getLongVersionCode()
+                            != parsedPackage.getLongVersionCode()) {
                         throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
                                 "Packages declaring "
                                         + "static-shared libs cannot be updated");
@@ -16599,8 +16268,8 @@
 
                 final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
 
-                final PackageParser.Package oldPackage;
-                final String pkgName11 = pkg.packageName;
+                final AndroidPackage oldPackage;
+                final String pkgName11 = parsedPackage.getPackageName();
                 final int[] allUsers;
                 final int[] installedUsers;
 
@@ -16608,8 +16277,9 @@
                     oldPackage = mPackages.get(pkgName11);
                     existingPackage = oldPackage;
                     if (DEBUG_INSTALL) {
+                        // TODO(b/135203078): PackageImpl.toString()
                         Slog.d(TAG,
-                                "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
+                                "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
                     }
 
                     ps = mSettings.mPackages.get(pkgName11);
@@ -16618,17 +16288,18 @@
                     // verify signatures are valid
                     final KeySetManagerService ksms = mSettings.mKeySetManagerService;
                     if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
-                        if (!ksms.checkUpgradeKeySetLocked(ps, pkg)) {
+                        if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
                             throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                     "New package not signed by keys specified by upgrade-keysets: "
                                             + pkgName11);
                         }
                     } else {
                         // default to original signature matching
-                        if (!pkg.mSigningDetails.checkCapability(oldPackage.mSigningDetails,
+                        if (!parsedPackage.getSigningDetails().checkCapability(
+                                oldPackage.getSigningDetails(),
                                 SigningDetails.CertCapabilities.INSTALLED_DATA)
-                                && !oldPackage.mSigningDetails.checkCapability(
-                                pkg.mSigningDetails,
+                                && !oldPackage.getSigningDetails().checkCapability(
+                                parsedPackage.getSigningDetails(),
                                 SigningDetails.CertCapabilities.ROLLBACK)) {
                             throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                     "New package has a different signature: " + pkgName11);
@@ -16636,13 +16307,13 @@
                     }
 
                     // don't allow a system upgrade unless the upgrade hash matches
-                    if (oldPackage.restrictUpdateHash != null && oldPackage.isSystem()) {
+                    if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
                         final byte[] digestBytes;
                         try {
                             final MessageDigest digest = MessageDigest.getInstance("SHA-512");
-                            updateDigest(digest, new File(pkg.baseCodePath));
-                            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-                                for (String path : pkg.splitCodePaths) {
+                            updateDigest(digest, new File(parsedPackage.getBaseCodePath()));
+                            if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
+                                for (String path : parsedPackage.getSplitCodePaths()) {
                                     updateDigest(digest, new File(path));
                                 }
                             }
@@ -16651,21 +16322,25 @@
                             throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
                                     "Could not compute hash: " + pkgName11);
                         }
-                        if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
+                        if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
                             throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
                                     "New package fails restrict-update check: " + pkgName11);
                         }
                         // retain upgrade restriction
-                        pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
+                        parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
                     }
 
                     // Check for shared user id changes
-                    String invalidPackageName =
-                            getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
+                    String invalidPackageName = null;
+                    if (!Objects.equals(oldPackage.getSharedUserId(),
+                            parsedPackage.getSharedUserId())) {
+                        invalidPackageName = parsedPackage.getPackageName();
+                    }
+
                     if (invalidPackageName != null) {
                         throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                                 "Package " + invalidPackageName + " tried to change user "
-                                        + oldPackage.mSharedUserId);
+                                        + oldPackage.getSharedUserId());
                     }
 
                     // In case of rollback, remember per-user/profile install state
@@ -16698,10 +16373,10 @@
 
                 // Update what is removed
                 res.removedInfo = new PackageRemovedInfo(this);
-                res.removedInfo.uid = oldPackage.applicationInfo.uid;
-                res.removedInfo.removedPackage = oldPackage.packageName;
+                res.removedInfo.uid = oldPackage.getUid();
+                res.removedInfo.removedPackage = oldPackage.getPackageName();
                 res.removedInfo.installerPackageName = ps.installerPackageName;
-                res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
+                res.removedInfo.isStaticSharedLib = parsedPackage.getStaticSharedLibName() != null;
                 res.removedInfo.isUpdate = true;
                 res.removedInfo.origUsers = installedUsers;
                 res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
@@ -16710,52 +16385,6 @@
                     res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
                 }
 
-                childPackages = mSettings.getChildSettingsLPr(ps);
-                if (childPackages != null) {
-                    for (PackageSetting childPs : childPackages) {
-                        boolean childPackageUpdated = false;
-                        PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
-                        if (res.addedChildPackages != null) {
-                            PackageInstalledInfo childRes = res.addedChildPackages.get(
-                                    childPkg.packageName);
-                            if (childRes != null) {
-                                childRes.removedInfo.uid = childPkg.applicationInfo.uid;
-                                childRes.removedInfo.removedPackage = childPkg.packageName;
-                                if (childPs != null) {
-                                    childRes.removedInfo.installerPackageName =
-                                            childPs.installerPackageName;
-                                }
-                                childRes.removedInfo.isUpdate = true;
-                                childRes.removedInfo.installReasons =
-                                        res.removedInfo.installReasons;
-                                childPackageUpdated = true;
-                            }
-                        }
-                        if (!childPackageUpdated) {
-                            PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
-                            childRemovedRes.removedPackage = childPkg.packageName;
-                            if (childPs != null) {
-                                childRemovedRes.installerPackageName = childPs.installerPackageName;
-                            }
-                            childRemovedRes.isUpdate = false;
-                            childRemovedRes.dataRemoved = true;
-                            synchronized (mLock) {
-                                if (childPs != null) {
-                                    childRemovedRes.origUsers = childPs.queryInstalledUsers(
-                                            allUsers,
-                                            true);
-                                }
-                            }
-                            if (res.removedInfo.removedChildPackages == null) {
-                                res.removedInfo.removedChildPackages = new ArrayMap<>();
-                            }
-                            res.removedInfo.removedChildPackages.put(childPkg.packageName,
-                                    childRemovedRes);
-                        }
-                    }
-                }
-
-
                 sysPkg = (isSystemApp(oldPackage));
                 if (sysPkg) {
                     // Set the system/privileged/oem/vendor/product flags as needed
@@ -16774,41 +16403,30 @@
                             | (odm ? SCAN_AS_ODM : 0);
 
                     if (DEBUG_INSTALL) {
-                        Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+                        Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
                                 + ", old=" + oldPackage);
                     }
                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-                    pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
-                            ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+                    parsedPackage.setUpdatedSystemApp(true);
                     targetParseFlags = systemParseFlags;
                     targetScanFlags = systemScanFlags;
                 } else { // non system replace
                     replace = true;
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG,
-                                "replaceNonSystemPackageLI: new=" + pkg + ", old="
+                                "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
                                         + oldPackage);
                     }
-
-                    String pkgName1 = oldPackage.packageName;
-                    boolean deletedPkg = true;
-                    boolean addedPkg = false;
-                    boolean updatedSettings = false;
-
-                    final long origUpdateTime = (pkg.mExtras != null)
-                            ? ((PackageSetting) pkg.mExtras).lastUpdateTime : 0;
-
                 }
             } else { // new package install
                 ps = null;
-                childPackages = null;
                 disabledPs = null;
                 replace = false;
                 existingPackage = null;
                 // Remember this for later, in case we need to rollback this install
-                String pkgName1 = pkg.packageName;
+                String pkgName1 = parsedPackage.getPackageName();
 
-                if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
+                if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
 
                 // TODO(patb): MOVE TO RECONCILE
                 synchronized (mLock) {
@@ -16835,9 +16453,9 @@
             shouldCloseFreezerBeforeReturn = false;
 
             return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
-                    args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
-                    ps, disabledPs, childPackages);
+                    args.user, replace, targetScanFlags, targetParseFlags, existingPackage,
+                    parsedPackage, replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+                    ps, disabledPs);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
@@ -16852,7 +16470,7 @@
      * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
      * kernel patches). In normal mode, all file format can be supported.
      */
-    private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException,
+    private void setUpFsVerityIfPossible(AndroidPackage pkg) throws InstallerException,
             PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
         final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
         final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
@@ -16864,11 +16482,11 @@
         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
         if (legacyMode) {
             synchronized (mLock) {
-                final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+                final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
                 if (ps != null && ps.isPrivileged()) {
-                    fsverityCandidates.put(pkg.baseCodePath, null);
-                    if (pkg.splitCodePaths != null) {
-                        for (String splitPath : pkg.splitCodePaths) {
+                    fsverityCandidates.put(pkg.getBaseCodePath(), null);
+                    if (pkg.getSplitCodePaths() != null) {
+                        for (String splitPath : pkg.getSplitCodePaths()) {
                             fsverityCandidates.put(splitPath, null);
                         }
                     }
@@ -16877,16 +16495,17 @@
         } else {
             // NB: These files will become only accessible if the signing key is loaded in kernel's
             // .fs-verity keyring.
-            fsverityCandidates.put(pkg.baseCodePath,
-                    VerityUtils.getFsveritySignatureFilePath(pkg.baseCodePath));
+            fsverityCandidates.put(pkg.getBaseCodePath(),
+                    VerityUtils.getFsveritySignatureFilePath(pkg.getBaseCodePath()));
 
-            final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(pkg.baseCodePath);
+            final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
+                    pkg.getBaseCodePath());
             if (new File(dmPath).exists()) {
                 fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
             }
 
-            if (pkg.splitCodePaths != null) {
-                for (String path : pkg.splitCodePaths) {
+            if (pkg.getSplitCodePaths() != null) {
+                for (String path : pkg.getSplitCodePaths()) {
                     fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
 
                     final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
@@ -16940,8 +16559,7 @@
         }
     }
 
-    private void startIntentFilterVerifications(int userId, boolean replacing,
-            PackageParser.Package pkg) {
+    private void startIntentFilterVerifications(int userId, boolean replacing, AndroidPackage pkg) {
         if (mIntentFilterVerifierComponent == null) {
             Slog.w(TAG, "No IntentFilter verification will not be done as "
                     + "there is no IntentFilterVerifier available!");
@@ -16954,29 +16572,29 @@
                 (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
 
         Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
-        msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);
+        msg.obj = new IFVerificationParams(
+                pkg.getPackageName(),
+                hasDomainURLs(pkg),
+                pkg.getActivities(),
+                replacing,
+                userId,
+                verifierUid
+        );
         mHandler.sendMessage(msg);
-
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageParser.Package childPkg = pkg.childPackages.get(i);
-            msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
-            msg.obj = new IFVerificationParams(childPkg, replacing, userId, verifierUid);
-            mHandler.sendMessage(msg);
-        }
     }
 
     private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
-            PackageParser.Package pkg) {
-        int size = pkg.activities.size();
+            String packageName,
+            boolean hasDomainUrls,
+            List<ParsedActivity> activities) {
+        int size = activities.size();
         if (size == 0) {
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                     "No activity, so no need to verify any IntentFilter!");
             return;
         }
 
-        final boolean hasDomainURLs = hasDomainURLs(pkg);
-        if (!hasDomainURLs) {
+        if (!hasDomainUrls) {
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                     "No domain URLs, so no need to verify any IntentFilter!");
             return;
@@ -16987,7 +16605,6 @@
                 + " Activities needs verification ...");
 
         int count = 0;
-        final String packageName = pkg.packageName;
 
         synchronized (mLock) {
             // If this is a new install and we see that we've already run verification for this
@@ -17006,8 +16623,8 @@
 
             // If any filters need to be verified, then all need to be.
             boolean needToVerify = false;
-            for (PackageParser.Activity a : pkg.activities) {
-                for (ActivityIntentInfo filter : a.intents) {
+            for (ParsedActivity a : activities) {
+                for (ParsedActivityIntentInfo filter : a.intents) {
                     if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
                             Slog.d(TAG,
@@ -17021,8 +16638,8 @@
 
             if (needToVerify) {
                 final int verificationId = mIntentFilterVerificationToken++;
-                for (PackageParser.Activity a : pkg.activities) {
-                    for (ActivityIntentInfo filter : a.intents) {
+                for (ParsedActivity a : activities) {
+                    for (ParsedActivityIntentInfo filter : a.intents) {
                         if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
                             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                                     "Verification needed for IntentFilter:" + filter.toString());
@@ -17048,9 +16665,8 @@
     }
 
     @GuardedBy("mLock")
-    private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) {
-        final ComponentName cn  = filter.activity.getComponentName();
-        final String packageName = cn.getPackageName();
+    private boolean needsNetworkVerificationLPr(ParsedActivityIntentInfo filter) {
+        final String packageName = filter.getPackageName();
 
         IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
                 packageName);
@@ -17070,45 +16686,45 @@
         }
     }
 
-    private static boolean isExternal(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+    private static boolean isExternal(AndroidPackage pkg) {
+        return (pkg.getFlags() & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
     private static boolean isExternal(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    static boolean isSystemApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    static boolean isSystemApp(AndroidPackage pkg) {
+        return (pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
-    private static boolean isPrivilegedApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+    private static boolean isPrivilegedApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
-    private static boolean isOemApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    private static boolean isOemApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
     }
 
-    private static boolean isVendorApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+    private static boolean isVendorApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
-    private static boolean isProductApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+    private static boolean isProductApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
     }
 
-    private static boolean isSystemExtApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags
+    private static boolean isSystemExtApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags()
                 & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
     }
 
-    private static boolean isOdmApp(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+    private static boolean isOdmApp(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
     }
 
-    private static boolean hasDomainURLs(PackageParser.Package pkg) {
-        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
+    private static boolean hasDomainURLs(AndroidPackage pkg) {
+        return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
     }
 
     private static boolean isSystemApp(PackageSetting ps) {
@@ -17119,12 +16735,12 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
-    private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
+    private VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
         if (isExternal(pkg)) {
-            if (TextUtils.isEmpty(pkg.volumeUuid)) {
+            if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
                 return mSettings.getExternalVersion();
             } else {
-                return mSettings.findOrCreateVersion(pkg.volumeUuid);
+                return mSettings.findOrCreateVersion(pkg.getVolumeUuid());
             }
         } else {
             return mSettings.getInternalVersion();
@@ -17132,6 +16748,7 @@
     }
 
     private void deleteTempPackageFiles() {
+        // TODO: Is this used?
         final FilenameFilter filter =
                 (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
     }
@@ -17265,11 +16882,11 @@
         });
     }
 
-    private String resolveExternalPackageNameLPr(PackageParser.Package pkg) {
-        if (pkg.staticSharedLibName != null) {
-            return pkg.manifestPackageName;
+    private String resolveExternalPackageNameLPr(AndroidPackage pkg) {
+        if (pkg.getStaticSharedLibName() != null) {
+            return pkg.getManifestPackageName();
         }
-        return pkg.packageName;
+        return pkg.getPackageName();
     }
 
     @GuardedBy("mLock")
@@ -17476,7 +17093,7 @@
 
         final PackageSetting uninstalledPs;
         final PackageSetting disabledSystemPs;
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
 
         // for the uninstall-updates case and restricted profiles, remember the per-
         // user handle installed state
@@ -17508,9 +17125,9 @@
 
             allUsers = mUserManager.getUserIds();
 
-            if (pkg != null && pkg.staticSharedLibName != null) {
-                SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(pkg.staticSharedLibName,
-                        pkg.staticSharedLibVersion);
+            if (pkg != null && pkg.getStaticSharedLibName() != null) {
+                SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
+                        pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
                 if (libraryInfo != null) {
                     for (int currUserId : allUsers) {
                         if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
@@ -17519,7 +17136,7 @@
                         List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
                                 libraryInfo, 0, currUserId);
                         if (!ArrayUtils.isEmpty(libClientPackages)) {
-                            Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+                            Slog.w(TAG, "Not removing package " + pkg.getManifestPackageName()
                                     + " hosting lib " + libraryInfo.getName() + " version "
                                     + libraryInfo.getLongVersion() + " used by " + libClientPackages
                                     + " for user " + currUserId);
@@ -17574,13 +17191,13 @@
             if (info.args != null) {
                 info.args.doPostDeleteLI(true);
             }
-            final PackageParser.Package stubPkg =
+            final AndroidPackage stubPkg =
                     (disabledSystemPs == null) ? null : disabledSystemPs.pkg;
-            if (stubPkg != null && stubPkg.isStub) {
+            if (stubPkg != null && stubPkg.isStub()) {
                 synchronized (mLock) {
                     // restore the enabled state of the stub; the state is overwritten when
                     // the stub is uninstalled
-                    final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
+                    final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.getPackageName());
                     if (stubPs != null) {
                         stubPs.setEnabled(origEnabledState, userId, "android");
                     }
@@ -17589,7 +17206,7 @@
                         || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
                     if (DEBUG_COMPRESSION) {
                         Slog.i(TAG, "Enabling system stub after removal; pkg: "
-                                + stubPkg.packageName);
+                                + stubPkg.getPackageName());
                     }
                     enableCompressedPackage(stubPkg);
                 }
@@ -17617,7 +17234,6 @@
         boolean isStaticSharedLib;
         // Clean up resources deleted packages.
         InstallArgs args = null;
-        ArrayMap<String, PackageRemovedInfo> removedChildPackages;
         ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
 
         PackageRemovedInfo(PackageSender packageSender) {
@@ -17626,24 +17242,11 @@
 
         void sendPackageRemovedBroadcasts(boolean killApp) {
             sendPackageRemovedBroadcastInternal(killApp);
-            final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0;
-            for (int i = 0; i < childCount; i++) {
-                PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
-                childInfo.sendPackageRemovedBroadcastInternal(killApp);
-            }
         }
 
         void sendSystemPackageUpdatedBroadcasts() {
             if (isRemovedPackageSystemUpdate) {
                 sendSystemPackageUpdatedBroadcastsInternal();
-                final int childCount = (removedChildPackages != null)
-                        ? removedChildPackages.size() : 0;
-                for (int i = 0; i < childCount; i++) {
-                    PackageRemovedInfo childInfo = removedChildPackages.valueAt(i);
-                    if (childInfo.isRemovedPackageSystemUpdate) {
-                        childInfo.sendSystemPackageUpdatedBroadcastsInternal();
-                    }
-                }
             }
         }
 
@@ -17755,12 +17358,12 @@
         String packageName = deletedPs.name;
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
         // Retrieve object to delete permissions for shared user later on
-        final PackageParser.Package deletedPkg = deletedPs.pkg;
+        final AndroidPackage deletedPkg = deletedPs.pkg;
         if (outInfo != null) {
             outInfo.removedPackage = packageName;
             outInfo.installerPackageName = deletedPs.installerPackageName;
             outInfo.isStaticSharedLib = deletedPkg != null
-                    && deletedPkg.staticSharedLibName != null;
+                    && deletedPkg.getStaticSharedLibName() != null;
             outInfo.populateUsers(deletedPs == null ? null
                     : deletedPs.queryInstalledUsers(mUserManager.getUserIds(), true), deletedPs);
         }
@@ -17768,14 +17371,14 @@
         removePackageLI(deletedPs.name, (flags & PackageManager.DELETE_CHATTY) != 0);
 
         if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
-            final PackageParser.Package resolvedPkg;
+            final AndroidPackage resolvedPkg;
             if (deletedPkg != null) {
                 resolvedPkg = deletedPkg;
             } else {
                 // We don't have a parsed package when it lives on an ejected
                 // adopted storage device, so fake something together
-                resolvedPkg = new PackageParser.Package(deletedPs.name);
-                resolvedPkg.setVolumeUuid(deletedPs.volumeUuid);
+                resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.name,
+                        deletedPs.volumeUuid);
             }
             destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
                     FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
@@ -17887,13 +17490,13 @@
             throws SystemDeleteException {
         final boolean applyUserRestrictions =
                 (allUserHandles != null) && outInfo != null && (outInfo.origUsers != null);
-        final PackageParser.Package deletedPkg = deletedPs.pkg;
+        final AndroidPackage deletedPkg = deletedPs.pkg;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
         // the system pkg from system partition
         // reader
         final PackageSetting disabledPs = action.disabledPs;
-        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
+        if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
                 + " disabledPs=" + disabledPs);
         Slog.d(TAG, "Deleting system pkg from data partition");
 
@@ -17910,21 +17513,6 @@
         if (outInfo != null) {
             // Delete the updated package
             outInfo.isRemovedPackageSystemUpdate = true;
-            if (outInfo.removedChildPackages != null) {
-                final int childCount = (deletedPs.childPackageNames != null)
-                        ? deletedPs.childPackageNames.size() : 0;
-                for (int i = 0; i < childCount; i++) {
-                    String childPackageName = deletedPs.childPackageNames.get(i);
-                    if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
-                            .contains(childPackageName)) {
-                        PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
-                                childPackageName);
-                        if (childInfo != null) {
-                            childInfo.isRemovedPackageSystemUpdate = true;
-                        }
-                    }
-                }
-            }
         }
 
         if (disabledPs.versionCode < deletedPs.versionCode) {
@@ -17936,7 +17524,7 @@
         }
 
         deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
-                outInfo, writeSettings, disabledPs.pkg);
+                outInfo, writeSettings);
 
         // writer
         synchronized (mLock) {
@@ -17957,16 +17545,16 @@
                     outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
                     writeSettings);
         } catch (PackageManagerException e) {
-            Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+            Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
                     + e.getMessage());
             // TODO(patb): can we avoid this; throw would come from scan...
             throw new SystemDeleteException(e);
         } finally {
-            if (disabledPs.pkg.isStub) {
+            if (disabledPs.pkg.isStub()) {
                 // We've re-installed the stub; make sure it's disabled here. If package was
                 // originally enabled, we'll install the compressed version of the application
                 // and re-enable it afterward.
-                final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.packageName);
+                final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
                 if (stubPs != null) {
                     stubPs.setEnabled(
                             COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
@@ -17978,7 +17566,7 @@
     /**
      * Installs a package that's already on the system partition.
      */
-    private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
+    private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString,
             @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
             @Nullable PermissionsState origPermissionState, boolean writeSettings)
                     throws PackageManagerException {
@@ -17999,7 +17587,7 @@
         }
 
         final File codePath = new File(codePathString);
-        final PackageParser.Package pkg =
+        final AndroidPackage pkg =
                 scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
 
         try {
@@ -18013,7 +17601,7 @@
 
         // writer
         synchronized (mLock) {
-            PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+            PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
 
             // Propagate the permissions state as we do not want to drop on the floor
             // runtime permissions. The update permissions method below will take
@@ -18021,7 +17609,7 @@
             if (origPermissionState != null) {
                 ps.getPermissionsState().copyFrom(origPermissionState);
             }
-            mPermissionManager.updatePermissions(pkg.packageName, pkg);
+            mPermissionManager.updatePermissions(pkg.getPackageName(), pkg);
 
             final boolean applyUserRestrictions
                     = (allUserHandles != null) && (origUserHandles != null);
@@ -18059,58 +17647,22 @@
 
     private void deleteInstalledPackageLIF(PackageSetting ps,
             boolean deleteCodeAndResources, int flags, int[] allUserHandles,
-            PackageRemovedInfo outInfo, boolean writeSettings,
-            PackageParser.Package replacingPackage) {
+            PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mLock) {
             if (outInfo != null) {
                 outInfo.uid = ps.appId;
             }
-
-            if (outInfo != null && outInfo.removedChildPackages != null) {
-                final int childCount = (ps.childPackageNames != null)
-                        ? ps.childPackageNames.size() : 0;
-                for (int i = 0; i < childCount; i++) {
-                    String childPackageName = ps.childPackageNames.get(i);
-                    PackageSetting childPs = mSettings.mPackages.get(childPackageName);
-                    PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
-                            childPackageName);
-                    if (childInfo != null) {
-                        childInfo.uid = childPs.appId;
-                    }
-                }
-            }
         }
 
         // Delete package data from internal structures and also remove data if flag is set
         removePackageDataLIF(ps, allUserHandles, outInfo, flags, writeSettings);
 
-        // Delete the child packages data
-        final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            PackageSetting childPs;
-            synchronized (mLock) {
-                childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
-            }
-            if (childPs != null) {
-                PackageRemovedInfo childOutInfo = (outInfo != null
-                        && outInfo.removedChildPackages != null)
-                        ? outInfo.removedChildPackages.get(childPs.name) : null;
-                final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0
-                        && (replacingPackage != null
-                        && !replacingPackage.hasChildPackage(childPs.name))
-                        ? flags & ~DELETE_KEEP_DATA : flags;
-                removePackageDataLIF(childPs, allUserHandles, childOutInfo,
-                        deleteFlags, writeSettings);
-            }
-        }
-
         // Delete application code and resources only for parent packages
-        if (ps.parentPackageName == null) {
-            if (deleteCodeAndResources && (outInfo != null)) {
-                outInfo.args = createInstallArgsForExisting(
-                        ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
-                if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
-            }
+        if (deleteCodeAndResources && (outInfo != null)) {
+            outInfo.args = createInstallArgsForExisting(
+                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(
+                            ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
+            if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
     }
 
@@ -18123,10 +17675,10 @@
             // Cannot block uninstall of static shared libs as they are
             // considered a part of the using app (emulating static linking).
             // Also static libs are installed always on internal storage.
-            PackageParser.Package pkg = mPackages.get(packageName);
-            if (pkg != null && pkg.staticSharedLibName != null) {
+            AndroidPackage pkg = mPackages.get(packageName);
+            if (pkg != null && pkg.getStaticSharedLibName() != null) {
                 Slog.w(TAG, "Cannot block uninstall of package: " + packageName
-                        + " providing static shared library: " + pkg.staticSharedLibName);
+                        + " providing static shared library: " + pkg.getStaticSharedLibName());
                 return false;
             }
             mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
@@ -18190,40 +17742,22 @@
     @GuardedBy("mLock")
     private static DeletePackageAction mayDeletePackageLocked(
             PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
-            @Nullable PackageSetting[] children, int flags, UserHandle user) {
+            int flags, UserHandle user) {
         if (ps == null) {
             return null;
         }
         if (isSystemApp(ps)) {
-            if (ps.parentPackageName != null) {
-                Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
-                return null;
-            }
-
             final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0;
             final boolean deleteAllUsers =
                     user == null || user.getIdentifier() == UserHandle.USER_ALL;
             if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
-                Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.packageName);
+                Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.getPackageName());
                 return null;
             }
             // Confirmed if the system package has been updated
             // An updated system app can be deleted. This will also have to restore
             // the system pkg from system partition reader
         }
-        final int parentReferenceCount =
-                (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
-        final int childCount = children != null ? children.length : 0;
-        if (childCount != parentReferenceCount) {
-            return null;
-        }
-        if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
-            for (PackageSetting child : children) {
-                if (child == null || !ps.childPackageNames.contains(child.name)) {
-                    return null;
-                }
-            }
-        }
         return new DeletePackageAction(ps, disabledPs, outInfo, flags, user);
     }
 
@@ -18233,13 +17767,12 @@
     private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
             boolean deleteCodeAndResources, int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings,
-            PackageParser.Package replacingPackage) {
+            ParsedPackage replacingPackage) {
         final DeletePackageAction action;
         synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
-            PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
-            action = mayDeletePackageLocked(outInfo, ps, disabledPs, children, flags, user);
+            action = mayDeletePackageLocked(outInfo, ps, disabledPs, flags, user);
         }
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
         if (null == action) {
@@ -18270,30 +17803,13 @@
     private void executeDeletePackageLIF(DeletePackageAction action,
             String packageName, boolean deleteCodeAndResources,
             int[] allUserHandles, boolean writeSettings,
-            PackageParser.Package replacingPackage) throws SystemDeleteException {
+            ParsedPackage replacingPackage) throws SystemDeleteException {
         final PackageSetting ps = action.deletingPs;
         final PackageRemovedInfo outInfo = action.outInfo;
         final UserHandle user = action.user;
         final int flags = action.flags;
         final boolean systemApp = isSystemApp(ps);
 
-        if (ps.parentPackageName != null
-                && (!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
-            if (DEBUG_REMOVE) {
-                Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
-                        + ((user == null) ? UserHandle.USER_ALL : user));
-            }
-            final int removedUserId = (user != null) ? user.getIdentifier()
-                    : UserHandle.USER_ALL;
-
-            clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags);
-            synchronized (mLock) {
-                markPackageUninstalledForUserLPw(ps, user);
-                scheduleWritePackageRestrictionsLocked(user);
-            }
-            return;
-        }
-
         final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
         if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
             unsuspendForSuspendingPackage(packageName, userId);
@@ -18343,26 +17859,6 @@
             }
         }
 
-        // If we are deleting a composite package for all users, keep track
-        // of result for each child.
-        if (ps.childPackageNames != null && outInfo != null) {
-            synchronized (mLock) {
-                final int childCount = ps.childPackageNames.size();
-                outInfo.removedChildPackages = new ArrayMap<>(childCount);
-                for (int i = 0; i < childCount; i++) {
-                    String childPackageName = ps.childPackageNames.get(i);
-                    PackageRemovedInfo childInfo = new PackageRemovedInfo(this);
-                    childInfo.removedPackage = childPackageName;
-                    childInfo.installerPackageName = ps.installerPackageName;
-                    outInfo.removedChildPackages.put(childPackageName, childInfo);
-                    PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
-                    if (childPs != null) {
-                        childInfo.origUsers = childPs.queryInstalledUsers(allUserHandles, true);
-                    }
-                }
-            }
-        }
-
         // TODO(b/109941548): break reasons for ret = false out into mayDelete method
         if (systemApp) {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
@@ -18372,53 +17868,12 @@
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
-                    outInfo, writeSettings, replacingPackage);
+                    outInfo, writeSettings);
         }
 
         // Take a note whether we deleted the package for all users
         if (outInfo != null) {
             outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
-            if (outInfo.removedChildPackages != null) {
-                synchronized (mLock) {
-                    final int childCount = outInfo.removedChildPackages.size();
-                    for (int i = 0; i < childCount; i++) {
-                        PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i);
-                        if (childInfo != null) {
-                            childInfo.removedForAllUsers = mPackages.get(
-                                    childInfo.removedPackage) == null;
-                        }
-                    }
-                }
-            }
-            // If we uninstalled an update to a system app there may be some
-            // child packages that appeared as they are declared in the system
-            // app but were not declared in the update.
-            if (systemApp) {
-                synchronized (mLock) {
-                    PackageSetting updatedPs = mSettings.getPackageLPr(ps.name);
-                    final int childCount = (updatedPs.childPackageNames != null)
-                            ? updatedPs.childPackageNames.size() : 0;
-                    for (int i = 0; i < childCount; i++) {
-                        String childPackageName = updatedPs.childPackageNames.get(i);
-                        if (outInfo.removedChildPackages == null
-                                || outInfo.removedChildPackages.indexOfKey(childPackageName) < 0) {
-                            PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
-                            if (childPs == null) {
-                                continue;
-                            }
-                            PackageInstalledInfo installRes = new PackageInstalledInfo();
-                            installRes.name = childPackageName;
-                            installRes.newUsers = childPs.queryInstalledUsers(allUserHandles, true);
-                            installRes.pkg = mPackages.get(childPackageName);
-                            installRes.uid = childPs.pkg.applicationInfo.uid;
-                            if (outInfo.appearedChildPackages == null) {
-                                outInfo.appearedChildPackages = new ArrayMap<>();
-                            }
-                            outInfo.appearedChildPackages.put(childPackageName, installRes);
-                        }
-                    }
-                }
-            }
         }
     }
 
@@ -18455,7 +17910,7 @@
 
     private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
             PackageRemovedInfo outInfo, int flags) {
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(ps.name);
         }
@@ -18497,7 +17952,7 @@
         if (outInfo != null) {
             outInfo.removedPackage = ps.name;
             outInfo.installerPackageName = ps.installerPackageName;
-            outInfo.isStaticSharedLib = pkg != null && pkg.staticSharedLibName != null;
+            outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
             outInfo.removedAppId = ps.appId;
             outInfo.removedUsers = userIds;
             outInfo.broadcastUsers = userIds;
@@ -18508,7 +17963,7 @@
     public void clearApplicationProfileData(String packageName) {
         enforceSystemOrRoot("Only the system can clear all profile data");
 
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
@@ -18586,7 +18041,7 @@
         }
 
         // Try finding details about the requested package
-        PackageParser.Package pkg;
+        AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
@@ -18605,7 +18060,7 @@
         clearAppDataLIF(pkg, userId,
                 FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
 
-        final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+        final int appId = UserHandle.getAppId(pkg.getUid());
         removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId);
 
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
@@ -18682,14 +18137,14 @@
         final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_INSTANT_APPS);
 
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
 
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(() -> {
-            final PackageSetting ps = pkg == null ? null : (PackageSetting) pkg.mExtras;
+            final PackageSetting ps = pkg == null ? null : getPackageSetting(pkg.getPackageName());
             boolean doClearData = true;
             if (ps != null) {
                 final boolean targetIsInstantApp =
@@ -18769,7 +18224,7 @@
             while (it.hasNext()) {
                 final PackageSetting ps = it.next();
                 if (ps.pkg != null) {
-                    int v = ps.pkg.applicationInfo.targetSdkVersion;
+                    int v = ps.pkg.getTargetSdkVersion();
                     if (v < vers) vers = v;
                 }
             }
@@ -18777,7 +18232,7 @@
         } else if (obj instanceof PackageSetting) {
             final PackageSetting ps = (PackageSetting) obj;
             if (ps.pkg != null) {
-                return ps.pkg.applicationInfo.targetSdkVersion;
+                return ps.pkg.getTargetSdkVersion();
             }
         }
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -18785,9 +18240,9 @@
 
     @GuardedBy("mLock")
     private int getPackageTargetSdkVersionLockedLPr(String packageName) {
-        final PackageParser.Package p = mPackages.get(packageName);
+        final AndroidPackage p = mPackages.get(packageName);
         if (p != null) {
-            return p.applicationInfo.targetSdkVersion;
+            return p.getTargetSdkVersion();
         }
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
@@ -18956,7 +18411,7 @@
         }
         // writer
         synchronized (mLock) {
-            PackageParser.Package pkg = mPackages.get(packageName);
+            AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null || !isCallerSameApp(packageName, callingUid)) {
                 if (mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
@@ -19030,8 +18485,8 @@
     private void clearIntentFilterVerificationsLPw(int userId) {
         final int packageCount = mPackages.size();
         for (int i = 0; i < packageCount; i++) {
-            PackageParser.Package pkg = mPackages.valueAt(i);
-            clearIntentFilterVerificationsLPw(pkg.packageName, userId);
+            AndroidPackage pkg = mPackages.valueAt(i);
+            clearIntentFilterVerificationsLPw(pkg.getPackageName(), userId);
         }
     }
 
@@ -19938,8 +19393,8 @@
             // If we're enabling a system stub, there's a little more work to do.
             // Prior to enabling the package, we need to decompress the APK(s) to the
             // data partition and then replace the version on the system partition.
-            final PackageParser.Package deletedPkg = pkgSetting.pkg;
-            final boolean isSystemStub = deletedPkg.isStub
+            final AndroidPackage deletedPkg = pkgSetting.pkg;
+            final boolean isSystemStub = deletedPkg.isStub()
                     && deletedPkg.isSystem();
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
@@ -19960,10 +19415,10 @@
             synchronized (mLock) {
                 // We're dealing with a component level state change
                 // First, verify that this is a valid class name.
-                PackageParser.Package pkg = pkgSetting.pkg;
+                AndroidPackage pkg = pkgSetting.pkg;
                 if (pkg == null || !pkg.hasComponentClassName(className)) {
                     if (pkg != null &&
-                            pkg.applicationInfo.targetSdkVersion >=
+                            pkg.getTargetSdkVersion() >=
                                     Build.VERSION_CODES.JELLY_BEAN) {
                         throw new IllegalArgumentException("Component class " + className
                                 + " does not exist in " + packageName);
@@ -20903,7 +20358,7 @@
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
                     && packageName == null) {
-                mComponentResolver.dumpServicePermissions(pw, dumpState, packageName);
+                mComponentResolver.dumpServicePermissions(pw, dumpState);
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
@@ -21046,9 +20501,9 @@
         ipw.println();
         ipw.println("Dexopt state:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages;
+        Collection<AndroidPackage> packages;
         if (packageName != null) {
-            PackageParser.Package targetPackage = mPackages.get(packageName);
+            AndroidPackage targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
                 packages = Collections.singletonList(targetPackage);
             } else {
@@ -21059,11 +20514,11 @@
             packages = mPackages.values();
         }
 
-        for (PackageParser.Package pkg : packages) {
-            ipw.println("[" + pkg.packageName + "]");
+        for (AndroidPackage pkg : packages) {
+            ipw.println("[" + pkg.getPackageName() + "]");
             ipw.increaseIndent();
             mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
-                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
+                    mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
             ipw.decreaseIndent();
         }
     }
@@ -21075,9 +20530,9 @@
         ipw.println();
         ipw.println("Compiler stats:");
         ipw.increaseIndent();
-        Collection<PackageParser.Package> packages;
+        Collection<AndroidPackage> packages;
         if (packageName != null) {
-            PackageParser.Package targetPackage = mPackages.get(packageName);
+            AndroidPackage targetPackage = mPackages.get(packageName);
             if (targetPackage != null) {
                 packages = Collections.singletonList(targetPackage);
             } else {
@@ -21088,11 +20543,11 @@
             packages = mPackages.values();
         }
 
-        for (PackageParser.Package pkg : packages) {
-            ipw.println("[" + pkg.packageName + "]");
+        for (AndroidPackage pkg : packages) {
+            ipw.println("[" + pkg.getPackageName() + "]");
             ipw.increaseIndent();
 
-            CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
+            CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName());
             if (stats == null) {
                 ipw.println("(No recorded stats)");
             } else {
@@ -21163,14 +20618,14 @@
     }
 
     private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
-            ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) {
-        final int size = infos.size();
+            ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
+        final int size = packages.size();
         final String[] packageNames = new String[size];
         final int[] packageUids = new int[size];
         for (int i = 0; i < size; i++) {
-            final ApplicationInfo info = infos.get(i);
-            packageNames[i] = info.packageName;
-            packageUids[i] = info.uid;
+            final AndroidPackage pkg = packages.get(i);
+            packageNames[i] = pkg.getAppInfoPackageName();
+            packageUids[i] = pkg.getUid();
         }
         sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
                 finishedReceiver);
@@ -21213,7 +20668,7 @@
         }
 
         final ArrayList<PackageFreezer> freezers = new ArrayList<>();
-        final ArrayList<ApplicationInfo> loaded = new ArrayList<>();
+        final ArrayList<AndroidPackage> loaded = new ArrayList<>();
         final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
 
         final VersionInfo ver;
@@ -21226,10 +20681,10 @@
         for (PackageSetting ps : packages) {
             freezers.add(freezePackage(ps.name, "loadPrivatePackagesInner"));
             synchronized (mInstallLock) {
-                final PackageParser.Package pkg;
+                final AndroidPackage pkg;
                 try {
                     pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
-                    loaded.add(pkg.applicationInfo);
+                    loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
                     Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
@@ -21301,14 +20756,14 @@
             return;
         }
 
-        final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
+        final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
         synchronized (mLock) {
             final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
             for (PackageSetting ps : packages) {
                 if (ps.pkg == null) continue;
 
-                final ApplicationInfo info = ps.pkg.applicationInfo;
+                final AndroidPackage pkg = ps.pkg;
                 final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
                 final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
 
@@ -21316,7 +20771,7 @@
                         "unloadPrivatePackagesInner")) {
                     if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
                             false, null)) {
-                        unloaded.add(info);
+                        unloaded.add(pkg);
                     } else {
                         Slog.w(TAG, "Failed to unload " + ps.codePath);
                     }
@@ -21531,7 +20986,7 @@
                 continue;
             }
             // Skip non-core apps if requested
-            if (onlyCoreApps && !ps.pkg.coreApp) {
+            if (onlyCoreApps && !ps.pkg.isCoreApp()) {
                 result.add(packageName);
                 continue;
             }
@@ -21558,10 +21013,10 @@
      * <p>
      * <em>Note: To avoid a deadlock, do not call this method with {@code mLock} lock held</em>
      */
-    private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
+    private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
         final PackageSetting ps;
         synchronized (mLock) {
-            ps = mSettings.mPackages.get(pkg.packageName);
+            ps = mSettings.mPackages.get(pkg.getPackageName());
             mSettings.writeKernelMappingLPr(ps);
         }
 
@@ -21591,19 +21046,15 @@
      * will try recovering system apps by wiping data; third-party app data is
      * left intact.
      */
-    private void prepareAppDataLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         prepareAppDataLeafLIF(pkg, userId, flags);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            prepareAppDataLeafLIF(pkg.childPackages.get(i), userId, flags);
-        }
     }
 
-    private void prepareAppDataAndMigrateLIF(PackageParser.Package pkg, int userId, int flags,
+    private void prepareAppDataAndMigrateLIF(AndroidPackage pkg, int userId, int flags,
             boolean maybeMigrateAppData) {
         prepareAppDataLIF(pkg, userId, flags);
 
@@ -21614,43 +21065,37 @@
         }
     }
 
-    private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
         if (DEBUG_APP_DATA) {
-            Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x"
+            Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
                     + Integer.toHexString(flags));
         }
 
         final PackageSetting ps;
         synchronized (mLock) {
-            ps = mSettings.mPackages.get(pkg.packageName);
+            ps = mSettings.mPackages.get(pkg.getPackageName());
         }
-        final String volumeUuid = pkg.volumeUuid;
-        final String packageName = pkg.packageName;
+        final String volumeUuid = pkg.getVolumeUuid();
+        final String packageName = pkg.getPackageName();
 
-        ApplicationInfo app = (ps == null)
-                ? pkg.applicationInfo
-                : PackageParser.generateApplicationInfo(pkg, 0, ps.readUserState(userId), userId);
-        if (app == null) {
-            app = pkg.applicationInfo;
-        }
+        final int appId = UserHandle.getAppId(pkg.getUid());
 
-        final int appId = UserHandle.getAppId(app.uid);
+        Preconditions.checkNotNull(pkg.getSeInfo());
 
-        Preconditions.checkNotNull(app.seInfo);
-
-        final String seInfo = app.seInfo + (app.seInfoUser != null ? app.seInfoUser : "");
+        final String seInfo =
+                pkg.getSeInfo() + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
         long ceDataInode = -1;
         try {
             ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
-                    appId, seInfo, app.targetSdkVersion);
+                    appId, seInfo, pkg.getTargetSdkVersion());
         } catch (InstallerException e) {
-            if (app.isSystemApp()) {
+            if (pkg.isSystemApp()) {
                 logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
                         + ", but trying to recover: " + e);
                 destroyAppDataLeafLIF(pkg, userId, flags);
                 try {
                     ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
-                            appId, seInfo, app.targetSdkVersion);
+                            appId, seInfo, pkg.getTargetSdkVersion());
                     logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
                 } catch (InstallerException e2) {
                     logCriticalInfo(Log.DEBUG, "Recovery failed!");
@@ -21692,29 +21137,24 @@
         prepareAppDataContentsLeafLIF(pkg, userId, flags);
     }
 
-    private void prepareAppDataContentsLIF(PackageParser.Package pkg, int userId, int flags) {
+    private void prepareAppDataContentsLIF(AndroidPackage pkg, int userId, int flags) {
         if (pkg == null) {
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
         prepareAppDataContentsLeafLIF(pkg, userId, flags);
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            prepareAppDataContentsLeafLIF(pkg.childPackages.get(i), userId, flags);
-        }
     }
 
-    private void prepareAppDataContentsLeafLIF(PackageParser.Package pkg, int userId, int flags) {
-        final String volumeUuid = pkg.volumeUuid;
-        final String packageName = pkg.packageName;
-        final ApplicationInfo app = pkg.applicationInfo;
+    private void prepareAppDataContentsLeafLIF(AndroidPackage pkg, int userId, int flags) {
+        final String volumeUuid = pkg.getVolumeUuid();
+        final String packageName = pkg.getPackageName();
 
         if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
             // Create a native library symlink only if we have native libraries
             // and if the native libraries are 32 bit libraries. We do not provide
             // this symlink for 64 bit libraries.
-            if (app.primaryCpuAbi != null && !VMRuntime.is64BitAbi(app.primaryCpuAbi)) {
-                final String nativeLibPath = app.nativeLibraryDir;
+            if (pkg.getPrimaryCpuAbi() != null && !VMRuntime.is64BitAbi(pkg.getPrimaryCpuAbi())) {
+                final String nativeLibPath = pkg.getNativeLibraryDir();
                 try {
                     mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
                             nativeLibPath, userId);
@@ -21730,17 +21170,17 @@
      * CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag
      * requested by the app.
      */
-    private boolean maybeMigrateAppDataLIF(PackageParser.Package pkg, int userId) {
+    private boolean maybeMigrateAppDataLIF(AndroidPackage pkg, int userId) {
         if (pkg.isSystem() && !StorageManager.isFileEncryptedNativeOrEmulated()
                 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
-            final int storageTarget = pkg.applicationInfo.isDefaultToDeviceProtectedStorage()
+            final int storageTarget = pkg.isDefaultToDeviceProtectedStorage()
                     ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
             try {
-                mInstaller.migrateAppData(pkg.volumeUuid, pkg.packageName, userId,
+                mInstaller.migrateAppData(pkg.getVolumeUuid(), pkg.getPackageName(), userId,
                         storageTarget);
             } catch (InstallerException e) {
                 logCriticalInfo(Log.WARN,
-                        "Failed to migrate " + pkg.packageName + ": " + e.getMessage());
+                        "Failed to migrate " + pkg.getPackageName() + ": " + e.getMessage());
             }
             return true;
         } else {
@@ -21791,7 +21231,6 @@
      */
     private class PackageFreezer implements AutoCloseable {
         private final String mPackageName;
-        private final PackageFreezer[] mChildren;
 
         private final boolean mWeFroze;
 
@@ -21806,7 +21245,6 @@
          */
         public PackageFreezer() {
             mPackageName = null;
-            mChildren = null;
             mWeFroze = false;
             mCloseGuard.open("close");
         }
@@ -21820,18 +21258,6 @@
                 if (ps != null) {
                     killApplication(ps.name, ps.appId, userId, killReason);
                 }
-
-                final PackageParser.Package p = mPackages.get(packageName);
-                if (p != null && p.childPackages != null) {
-                    final int N = p.childPackages.size();
-                    mChildren = new PackageFreezer[N];
-                    for (int i = 0; i < N; i++) {
-                        mChildren[i] = new PackageFreezer(p.childPackages.get(i).packageName,
-                                userId, killReason);
-                    }
-                } else {
-                    mChildren = null;
-                }
             }
             mCloseGuard.open("close");
         }
@@ -21854,12 +21280,6 @@
                     if (mWeFroze) {
                         mFrozenPackages.remove(mPackageName);
                     }
-
-                    if (mChildren != null) {
-                        for (PackageFreezer freezer : mChildren) {
-                            freezer.close();
-                        }
-                    }
                 }
             }
         }
@@ -21914,14 +21334,14 @@
 
         // reader
         synchronized (mLock) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
+            final AndroidPackage pkg = mPackages.get(packageName);
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (pkg == null
                     || ps == null
                     || shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) {
                 throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
             }
-            if (pkg.applicationInfo.isSystemApp()) {
+            if (pkg.isSystemApp()) {
                 throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
                         "Cannot move system application");
             }
@@ -21936,7 +21356,7 @@
 
             currentVolumeUuid = ps.volumeUuid;
 
-            final File probe = new File(pkg.codePath);
+            final File probe = new File(pkg.getCodePath());
             final File probeOat = new File(probe, "oat");
             if (!probe.isDirectory() || !probeOat.isDirectory()) {
                 throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
@@ -21947,7 +21367,7 @@
                 throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
                         "Package already moved to " + volumeUuid);
             }
-            if (pkg.applicationInfo.isInternal() && isPackageDeviceAdminOnAnyUser(packageName)) {
+            if (pkg.isInternal() && isPackageDeviceAdminOnAnyUser(packageName)) {
                 throw new PackageManagerException(MOVE_FAILED_DEVICE_ADMIN,
                         "Device admin cannot be moved");
             }
@@ -21958,13 +21378,13 @@
             }
 
             isCurrentLocationExternal = isExternal(pkg);
-            codeFile = new File(pkg.codePath);
+            codeFile = new File(pkg.getCodePath());
             installerPackageName = ps.installerPackageName;
             packageAbiOverride = ps.cpuAbiOverrideString;
-            appId = UserHandle.getAppId(pkg.applicationInfo.uid);
-            seinfo = pkg.applicationInfo.seInfo;
-            label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo));
-            targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+            appId = UserHandle.getAppId(pkg.getUid());
+            seinfo = pkg.getSeInfo();
+            label = String.valueOf(pm.getApplicationLabel(pkg.toAppInfo()));
+            targetSdkVersion = pkg.getTargetSdkVersion();
             freezer = freezePackage(packageName, "movePackageInternal");
             installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
         }
@@ -22125,7 +21545,7 @@
      * @param packageName The package that was moved.
      */
     private void logAppMovedStorage(String packageName, boolean isPreviousLocationExternal) {
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
@@ -22133,8 +21553,8 @@
             return;
         }
 
-        final StorageManager storage = mInjector.getStorageManager();
-        VolumeInfo volume = storage.findVolumeByUuid(pkg.applicationInfo.storageUuid.toString());
+        final StorageManager storage = mInjector.getStorageManager();;
+        VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString());
         int packageExternalStorageType = getPackageExternalStorageType(volume, isExternal(pkg));
 
         if (!isPreviousLocationExternal && isExternal(pkg)) {
@@ -22248,7 +21668,7 @@
             if (ps.pkg == null) {
                 continue;
             }
-            final String packageName = ps.pkg.packageName;
+            final String packageName = ps.pkg.getPackageName();
             // Skip over if system app
             if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                 continue;
@@ -22377,13 +21797,13 @@
         if (packageName == null || alias == null) {
             return null;
         }
-        synchronized (mLock) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
+        synchronized(mLock) {
+            final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            final PackageSetting ps = getPackageSetting(pkg.getPackageName());
             if (shouldFilterApplicationLocked(
                     ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
                 Slog.w(TAG, "KeySet requested for filtered package: " + packageName);
@@ -22402,19 +21822,19 @@
         synchronized (mLock) {
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
-            final PackageParser.Package pkg = mPackages.get(packageName);
+            final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
+            final PackageSetting ps = getPackageSetting(pkg.getPackageName());
             if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 // filter and pretend the package doesn't exist
                 Slog.w(TAG, "KeySet requested for filtered package: " + packageName
                         + ", uid:" + callingUid);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            if (pkg.applicationInfo.uid != callingUid
+            if (pkg.getUid() != callingUid
                     && Process.SYSTEM_UID != callingUid) {
                 throw new SecurityException("May not access signing KeySet of other apps.");
             }
@@ -22432,11 +21852,11 @@
         if (packageName == null || ks == null) {
             return false;
         }
-        synchronized (mLock) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
+        synchronized(mLock) {
+            final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
-                    || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid,
-                            UserHandle.getUserId(callingUid))) {
+                    || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+                    callingUid, UserHandle.getUserId(callingUid))) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
@@ -22459,10 +21879,10 @@
             return false;
         }
         synchronized (mLock) {
-            final PackageParser.Package pkg = mPackages.get(packageName);
+            final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
-                    || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid,
-                            UserHandle.getUserId(callingUid))) {
+                    || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+                    callingUid, UserHandle.getUserId(callingUid))) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
@@ -22494,29 +21914,29 @@
      * Check and throw if the given before/after packages would be considered a
      * downgrade.
      */
-    private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after)
+    private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
             throws PackageManagerException {
         if (after.getLongVersionCode() < before.getLongVersionCode()) {
             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                     "Update version code " + after.versionCode + " is older than current "
                     + before.getLongVersionCode());
         } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
-            if (after.baseRevisionCode < before.baseRevisionCode) {
+            if (after.baseRevisionCode < before.getBaseRevisionCode()) {
                 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                         "Update base revision code " + after.baseRevisionCode
-                        + " is older than current " + before.baseRevisionCode);
+                        + " is older than current " + before.getBaseRevisionCode());
             }
 
             if (!ArrayUtils.isEmpty(after.splitNames)) {
                 for (int i = 0; i < after.splitNames.length; i++) {
                     final String splitName = after.splitNames[i];
-                    final int j = ArrayUtils.indexOf(before.splitNames, splitName);
+                    final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
                     if (j != -1) {
-                        if (after.splitRevisionCodes[i] < before.splitRevisionCodes[j]) {
+                        if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
                             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                                     "Update split " + splitName + " revision code "
                                     + after.splitRevisionCodes[i] + " is older than current "
-                                    + before.splitRevisionCodes[j]);
+                                    + before.getSplitRevisionCodes()[j]);
                         }
                     }
                 }
@@ -22706,13 +22126,13 @@
             if (packageSetting == null) {
                 return false;
             }
-            PackageParser.Package pkg = packageSetting.pkg;
+            AndroidPackage pkg = packageSetting.pkg;
             if (pkg == null) {
                 // May happen if package in on a removable sd card
                 return false;
             }
-            return pkg.mSigningDetails.hasAncestorOrSelf(mPlatformPackage.mSigningDetails)
-                    || mPlatformPackage.mSigningDetails.checkCapability(pkg.mSigningDetails,
+            return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
+                    || mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
                     PackageParser.SigningDetails.CertCapabilities.PERMISSION);
         }
 
@@ -22748,11 +22168,11 @@
 
         private SigningDetails getSigningDetails(@NonNull String packageName) {
             synchronized (mLock) {
-                PackageParser.Package p = mPackages.get(packageName);
+                AndroidPackage p = mPackages.get(packageName);
                 if (p == null) {
                     return null;
                 }
-                return p.mSigningDetails;
+                return p.getSigningDetails();
             }
         }
 
@@ -22783,28 +22203,25 @@
         }
 
         @Override
-        public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) {
+        public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
             synchronized (mLock) {
-                return PackageManagerService.this.shouldFilterApplicationLocked(
-                        (PackageSetting) pkg.mExtras, callingUid, userId);
+                PackageSetting ps = getPackageSetting(pkg.getPackageName());
+                return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
+                        userId);
             }
         }
 
         @Override
         public boolean filterAppAccess(String packageName, int callingUid, int userId) {
             synchronized (mLock) {
-                final PackageParser.Package pkg = mPackages.get(packageName);
-                if (pkg == null) {
-                    return false;
-                }
-                return PackageManagerService.this
-                        .shouldFilterApplicationLocked(
-                                (PackageSetting) pkg.mExtras, callingUid, userId);
+                PackageSetting ps = getPackageSetting(packageName);
+                return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
+                        userId);
             }
         }
 
         @Override
-        public PackageParser.Package getPackage(String packageName) {
+        public AndroidPackage getPackage(String packageName) {
             synchronized (mLock) {
                 packageName = resolveInternalPackageNameLPr(
                         packageName, PackageManager.VERSION_CODE_HIGHEST);
@@ -22813,10 +22230,10 @@
         }
 
         @Override
-        public PackageParser.Package getPackage(int uid) {
+        public AndroidPackage getPackage(int uid) {
             synchronized (mLock) {
                 final String[] packageNames = getPackagesForUid(uid);
-                PackageParser.Package pkg = null;
+                AndroidPackage pkg = null;
                 final int numPackages = packageNames == null ? 0 : packageNames.length;
                 for (int i = 0; pkg == null && i < numPackages; i++) {
                     pkg = mPackages.get(packageNames[i]);
@@ -22825,6 +22242,12 @@
             }
         }
 
+        @Nullable
+        @Override
+        public PackageSetting getPackageSetting(String packageName) {
+            return PackageManagerService.this.getPackageSetting(packageName);
+        }
+
         @Override
         public PackageList getPackageList(PackageListObserver observer) {
             synchronized (mLock) {
@@ -22849,17 +22272,19 @@
         }
 
         @Override
-        public PackageParser.Package getDisabledSystemPackage(String packageName) {
+        public Object getDisabledSystemPackage(@NonNull String packageName) {
             synchronized (mLock) {
-                final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
-                return (ps != null) ? ps.pkg : null;
+                return mSettings.getDisabledSystemPkgLPr(packageName);
             }
         }
 
         @Override
-        public @Nullable String getDisabledSystemPackageName(@NonNull String packageName) {
-            PackageParser.Package pkg = getDisabledSystemPackage(packageName);
-            return pkg == null ? null : pkg.packageName;
+        public @Nullable
+        String getDisabledSystemPackageName(@NonNull String packageName) {
+            PackageSetting disabledPkgSetting = (PackageSetting) getDisabledSystemPackage(
+                    packageName);
+            AndroidPackage disabledPkg = disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
+            return disabledPkg == null ? null : disabledPkg.getPackageName();
         }
 
         @Override
@@ -22930,7 +22355,7 @@
         @Override
         public boolean isPermissionsReviewRequired(String packageName, int userId) {
             synchronized (mLock) {
-                final PackageParser.Package pkg = mPackages.get(packageName);
+                final AndroidPackage pkg = mPackages.get(packageName);
                 if (pkg == null) {
                     return false;
                 }
@@ -23086,9 +22511,10 @@
         }
 
         @Override
-        public boolean isEnabledAndMatches(ComponentInfo info, int flags, int userId) {
+        public boolean isEnabledAndMatches(ParsedComponent component, int flags, int userId) {
             synchronized (mLock) {
-                return mSettings.isEnabledAndMatchLPr(info, flags, userId);
+                AndroidPackage pkg = getPackage(component.getPackageName());
+                return mSettings.isEnabledAndMatchLPr(pkg, component, flags, userId);
             }
         }
 
@@ -23105,10 +22531,10 @@
         }
 
         @Override
-        public boolean setInstalled(PackageParser.Package pkg, @UserIdInt int userId,
+        public boolean setInstalled(AndroidPackage pkg, @UserIdInt int userId,
                 boolean installed) {
             synchronized (mLock) {
-                final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+                final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName());
                 if (ps.getInstalled(userId) != installed) {
                     ps.setInstalled(installed, userId);
                     return true;
@@ -23130,21 +22556,21 @@
         public void grantImplicitAccess(int userId, Intent intent,
                 int callingUid, int targetAppId) {
             synchronized (mLock) {
-                final PackageParser.Package callingPackage = getPackage(callingUid);
-                final PackageParser.Package targetPackage =
+                final AndroidPackage callingPackage = getPackage(callingUid);
+                final AndroidPackage targetPackage =
                         getPackage(UserHandle.getUid(userId, targetAppId));
                 if (callingPackage == null || targetPackage == null) {
                     return;
                 }
 
-                final boolean instantApp = isInstantAppInternal(callingPackage.packageName, userId,
-                        callingUid);
+                final boolean instantApp = isInstantAppInternal(callingPackage.getPackageName(),
+                        userId, callingUid);
                 if (instantApp) {
                     mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
                             UserHandle.getAppId(callingUid), targetAppId);
                 } else {
-                    mAppsFilter.grantImplicitAccess(
-                            callingPackage.packageName, targetPackage.packageName, userId);
+                    mAppsFilter.grantImplicitAccess(callingPackage.getPackageName(),
+                            targetPackage.getPackageName(), userId);
                 }
             }
         }
@@ -23176,9 +22602,9 @@
         @Override
         public boolean isPackagePersistent(String packageName) {
             synchronized (mLock) {
-                PackageParser.Package pkg = mPackages.get(packageName);
+                AndroidPackage pkg = mPackages.get(packageName);
                 return pkg != null
-                        ? ((pkg.applicationInfo.flags&(ApplicationInfo.FLAG_SYSTEM
+                        ? ((pkg.getFlags() & (ApplicationInfo.FLAG_SYSTEM
                                         | ApplicationInfo.FLAG_PERSISTENT)) ==
                                 (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT))
                         : false;
@@ -23186,9 +22612,9 @@
         }
 
         @Override
-        public boolean isLegacySystemApp(PackageParser.Package pkg) {
+        public boolean isLegacySystemApp(AndroidPackage pkg) {
             synchronized (mLock) {
-                final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                final PackageSetting ps = getPackageSetting(pkg.getPackageName());
                 return mPromoteSystemApps
                         && ps.isSystem()
                         && mExistingSystemPackages.contains(ps.name);
@@ -23199,9 +22625,10 @@
         public List<PackageInfo> getOverlayPackages(int userId) {
             final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
             synchronized (mLock) {
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mOverlayTarget != null) {
-                        PackageInfo pkg = generatePackageInfo((PackageSetting)p.mExtras, 0, userId);
+                for (AndroidPackage p : mPackages.values()) {
+                    if (p.getOverlayTarget() != null) {
+                        PackageInfo pkg = generatePackageInfo(getPackageSetting(p.getPackageName()),
+                                0, userId);
                         if (pkg != null) {
                             overlayPackages.add(pkg);
                         }
@@ -23215,9 +22642,9 @@
         public List<String> getTargetPackageNames(int userId) {
             List<String> targetPackages = new ArrayList<>();
             synchronized (mLock) {
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mOverlayTarget == null) {
-                        targetPackages.add(p.packageName);
+                for (AndroidPackage p : mPackages.values()) {
+                    if (p.getOverlayTarget() == null) {
+                        targetPackages.add(p.getPackageName());
                     }
                 }
             }
@@ -23238,12 +22665,12 @@
                     overlayPaths = new ArrayList<>(N);
                     for (int i = 0; i < N; i++) {
                         final String packageName = overlayPackageNames.get(i);
-                        final PackageParser.Package pkg = mPackages.get(packageName);
+                        final AndroidPackage pkg = mPackages.get(packageName);
                         if (pkg == null) {
                             Slog.e(TAG, "failed to find package " + packageName);
                             return false;
                         }
-                        overlayPaths.add(pkg.baseCodePath);
+                        overlayPaths.add(pkg.getBaseCodePath());
                     }
                 }
 
@@ -23361,12 +22788,12 @@
         }
 
         @Override
-        public void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+        public void forEachPackage(Consumer<AndroidPackage> actionLocked) {
             PackageManagerService.this.forEachPackage(actionLocked);
         }
 
         @Override
-        public void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked,
+        public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
                 @UserIdInt int userId) {
             PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
         }
@@ -23415,7 +22842,7 @@
          */
         @Override
         public boolean compileLayouts(String packageName) {
-            PackageParser.Package pkg;
+            AndroidPackage pkg;
             synchronized (mLock) {
                 pkg = mPackages.get(packageName);
                 if (pkg == null) {
@@ -23522,12 +22949,12 @@
 
         @Override
         public boolean isCallerInstallerOfRecord(
-                @NonNull PackageParser.Package pkg, int callingUid) {
+                @NonNull AndroidPackage pkg, int callingUid) {
             synchronized (mLock) {
                 if (pkg == null) {
                     return false;
                 }
-                final PackageSetting packageSetting = (PackageSetting) pkg.mExtras;
+                final PackageSetting packageSetting = getPackageSetting(pkg.getPackageName());
                 if (packageSetting == null) {
                     return false;
                 }
@@ -23624,7 +23051,16 @@
         }
     }
 
-    void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+    @Nullable
+    public PackageSetting getPackageSetting(String packageName) {
+        synchronized (mPackages) {
+            packageName = resolveInternalPackageNameLPr(
+                    packageName, PackageManager.VERSION_CODE_HIGHEST);
+            return mSettings.mPackages.get(packageName);
+        }
+    }
+
+    void forEachPackage(Consumer<AndroidPackage> actionLocked) {
         synchronized (mLock) {
             int numPackages = mPackages.size();
             for (int i = 0; i < numPackages; i++) {
@@ -23633,13 +23069,13 @@
         }
     }
 
-    void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked,
+    void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
             @UserIdInt int userId) {
         synchronized (mLock) {
             int numPackages = mPackages.size();
             for (int i = 0; i < numPackages; i++) {
-                PackageParser.Package pkg = mPackages.valueAt(i);
-                PackageSetting setting = mSettings.getPackageLPr(pkg.packageName);
+                AndroidPackage pkg = mPackages.valueAt(i);
+                PackageSetting setting = mSettings.getPackageLPr(pkg.getPackageName());
                 if (setting == null || !setting.getInstalled(userId)) {
                     continue;
                 }
@@ -23656,7 +23092,7 @@
      * Return a <b>copy</b> of the collection of packages known to the package manager.
      * @return A copy of the values of mPackages.
      */
-    Collection<PackageParser.Package> getPackages() {
+    Collection<AndroidPackage> getPackages() {
         synchronized (mLock) {
             return new ArrayList<>(mPackages.values());
         }
@@ -23692,8 +23128,8 @@
         return mCompilerStats.getPackageStats(pkgName);
     }
 
-    public CompilerStats.PackageStats getOrCreateCompilerPackageStats(PackageParser.Package pkg) {
-        return getOrCreateCompilerPackageStats(pkg.packageName);
+    public CompilerStats.PackageStats getOrCreateCompilerPackageStats(AndroidPackage pkg) {
+        return getOrCreateCompilerPackageStats(pkg.getPackageName());
     }
 
     public CompilerStats.PackageStats getOrCreateCompilerPackageStats(String pkgName) {
@@ -23803,7 +23239,7 @@
 
     boolean canHaveOatDir(String packageName) {
         synchronized (mLock) {
-            PackageParser.Package p = mPackages.get(packageName);
+            AndroidPackage p = mPackages.get(packageName);
             if (p == null) {
                 return false;
             }
@@ -23811,11 +23247,11 @@
         }
     }
 
-    private String getOatDir(PackageParser.Package pkg) {
+    private String getOatDir(AndroidPackage pkg) {
         if (!pkg.canHaveOatDir()) {
             return null;
         }
-        File codePath = new File(pkg.codePath);
+        File codePath = new File(pkg.getCodePath());
         if (codePath.isDirectory()) {
             return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
         }
@@ -23826,11 +23262,12 @@
         final String[] instructionSets;
         final List<String> codePaths;
         final String oatDir;
-        final PackageParser.Package pkg;
+        final AndroidPackage pkg;
         synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
-        instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+        instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
+                pkg.getSecondaryCpuAbi());
         codePaths = pkg.getAllCodePaths();
         oatDir = getOatDir(pkg);
 
@@ -23849,19 +23286,19 @@
         Set<String> unusedPackages = new HashSet<>();
         long currentTimeInMillis = System.currentTimeMillis();
         synchronized (mLock) {
-            for (PackageParser.Package pkg : mPackages.values()) {
-                PackageSetting ps =  mSettings.mPackages.get(pkg.packageName);
+            for (AndroidPackage pkg : mPackages.values()) {
+                PackageSetting ps =  mSettings.mPackages.get(pkg.getPackageName());
                 if (ps == null) {
                     continue;
                 }
                 PackageDexUsage.PackageUseInfo packageUseInfo =
-                      getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
+                      getDexManager().getPackageUseInfoOrDefault(pkg.getPackageName());
                 if (PackageManagerServiceUtils
                         .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
                                 downgradeTimeThresholdMillis, packageUseInfo,
                                 pkg.getLatestPackageUseTimeInMills(),
                                 pkg.getLatestForegroundPackageUseTimeInMills())) {
-                    unusedPackages.add(pkg.packageName);
+                    unusedPackages.add(pkg.getPackageName());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ef47410..ded9a9c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -35,10 +35,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ApkParseUtils;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
@@ -120,7 +123,7 @@
     // Sort a list of apps by their last usage, most recently used apps first. The order of
     // packages without usage data is undefined (but they will be sorted after the packages
     // that do have usage data).
-    public static void sortPackagesByUsageDate(List<PackageParser.Package> pkgs,
+    public static void sortPackagesByUsageDate(List<AndroidPackage> pkgs,
             PackageManagerService packageManagerService) {
         if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
             return;
@@ -135,12 +138,12 @@
     // package will be removed from {@code packages} and added to {@code result} with its
     // dependencies. If usage data is available, the positive packages will be sorted by usage
     // data (with {@code sortTemp} as temporary storage).
-    private static void applyPackageFilter(Predicate<PackageParser.Package> filter,
-            Collection<PackageParser.Package> result,
-            Collection<PackageParser.Package> packages,
-            @NonNull List<PackageParser.Package> sortTemp,
+    private static void applyPackageFilter(Predicate<AndroidPackage> filter,
+            Collection<AndroidPackage> result,
+            Collection<AndroidPackage> packages,
+            @NonNull List<AndroidPackage> sortTemp,
             PackageManagerService packageManagerService) {
-        for (PackageParser.Package pkg : packages) {
+        for (AndroidPackage pkg : packages) {
             if (filter.test(pkg)) {
                 sortTemp.add(pkg);
             }
@@ -149,10 +152,10 @@
         sortPackagesByUsageDate(sortTemp, packageManagerService);
         packages.removeAll(sortTemp);
 
-        for (PackageParser.Package pkg : sortTemp) {
+        for (AndroidPackage pkg : sortTemp) {
             result.add(pkg);
 
-            Collection<PackageParser.Package> deps =
+            Collection<AndroidPackage> deps =
                     packageManagerService.findSharedNonSystemLibraries(pkg);
             if (!deps.isEmpty()) {
                 deps.removeAll(result);
@@ -166,50 +169,51 @@
 
     // Sort apps by importance for dexopt ordering. Important apps are given
     // more priority in case the device runs out of space.
-    public static List<PackageParser.Package> getPackagesForDexopt(
-            Collection<PackageParser.Package> packages,
+    public static List<AndroidPackage> getPackagesForDexopt(
+            Collection<AndroidPackage> packages,
             PackageManagerService packageManagerService) {
         return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
     }
 
-    public static List<PackageParser.Package> getPackagesForDexopt(
-            Collection<PackageParser.Package> packages,
+    public static List<AndroidPackage> getPackagesForDexopt(
+            Collection<AndroidPackage> packages,
             PackageManagerService packageManagerService,
             boolean debug) {
-        ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
-        LinkedList<PackageParser.Package> result = new LinkedList<>();
-        ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
+        ArrayList<AndroidPackage> remainingPkgs = new ArrayList<>(packages);
+        LinkedList<AndroidPackage> result = new LinkedList<>();
+        ArrayList<AndroidPackage> sortTemp = new ArrayList<>(remainingPkgs.size());
 
         // Give priority to core apps.
-        applyPackageFilter((pkg) -> pkg.coreApp, result, remainingPkgs, sortTemp,
+        applyPackageFilter((pkg) -> pkg.isCoreApp(), result, remainingPkgs, sortTemp,
                 packageManagerService);
 
         // Give priority to system apps that listen for pre boot complete.
         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
         final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
-        applyPackageFilter((pkg) -> pkgNames.contains(pkg.packageName), result, remainingPkgs,
+        applyPackageFilter((pkg) -> pkgNames.contains(pkg.getPackageName()), result, remainingPkgs,
                 sortTemp, packageManagerService);
 
         // Give priority to apps used by other apps.
         DexManager dexManager = packageManagerService.getDexManager();
         applyPackageFilter((pkg) ->
-                dexManager.getPackageUseInfoOrDefault(pkg.packageName)
+                dexManager.getPackageUseInfoOrDefault(pkg.getPackageName())
                         .isAnyCodePathUsedByOtherApps(),
                 result, remainingPkgs, sortTemp, packageManagerService);
 
         // Filter out packages that aren't recently used, add all remaining apps.
         // TODO: add a property to control this?
-        Predicate<PackageParser.Package> remainingPredicate;
+        Predicate<AndroidPackage> remainingPredicate;
         if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
             if (debug) {
                 Log.i(TAG, "Looking at historical package use");
             }
             // Get the package that was used last.
-            PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
+            AndroidPackage lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
                     Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
                             pkg2.getLatestForegroundPackageUseTimeInMills()));
             if (debug) {
-                Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
+                Log.i(TAG, "Taking package " + lastUsed.getPackageName()
+                        + " as reference in time use");
             }
             long estimatedPreviousSystemUseTime =
                     lastUsed.getLatestForegroundPackageUseTimeInMills();
@@ -285,13 +289,13 @@
         }
     }
 
-    public static String packagesToString(Collection<PackageParser.Package> c) {
+    public static String packagesToString(Collection<AndroidPackage> c) {
         StringBuilder sb = new StringBuilder();
-        for (PackageParser.Package pkg : c) {
+        for (AndroidPackage pkg : c) {
             if (sb.length() > 0) {
                 sb.append(", ");
             }
-            sb.append(pkg.packageName);
+            sb.append(pkg.getPackageName());
         }
         return sb.toString();
     }
@@ -309,16 +313,16 @@
         return false;
     }
 
-    public static long getLastModifiedTime(PackageParser.Package pkg) {
-        final File srcFile = new File(pkg.codePath);
+    public static long getLastModifiedTime(AndroidPackage pkg) {
+        final File srcFile = new File(pkg.getCodePath());
         if (!srcFile.isDirectory()) {
             return srcFile.lastModified();
         }
-        final File baseFile = new File(pkg.baseCodePath);
+        final File baseFile = new File(pkg.getBaseCodePath());
         long maxModifiedTime = baseFile.lastModified();
-        if (pkg.splitCodePaths != null) {
-            for (int i = pkg.splitCodePaths.length - 1; i >=0; --i) {
-                final File splitFile = new File(pkg.splitCodePaths[i]);
+        if (pkg.getSplitCodePaths() != null) {
+            for (int i = pkg.getSplitCodePaths().length - 1; i >=0; --i) {
+                final File splitFile = new File(pkg.getSplitCodePaths()[i]);
                 maxModifiedTime = Math.max(maxModifiedTime, splitFile.lastModified());
             }
         }
@@ -539,7 +543,7 @@
     private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
             PackageSetting disabledPkgSetting) {
         try {
-            PackageParser.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
+            ApkParseUtils.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
             if (pkgSetting.signatures.mSigningDetails.checkCapability(
                     disabledPkgSetting.signatures.mSigningDetails,
                     PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
@@ -905,8 +909,10 @@
      * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState}
      * could not be found, {@code null} will be returned.
      */
-    public static PermissionsState getPermissionsState(PackageParser.Package pkg) {
-        final PackageSetting packageSetting = (PackageSetting) pkg.mExtras;
+    public static PermissionsState getPermissionsState(
+            PackageManagerInternal packageManagerInternal, AndroidPackage pkg) {
+        final PackageSetting packageSetting =
+                (PackageSetting) packageManagerInternal.getPackageSetting(pkg.getPackageName());
         if (packageSetting == null) {
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3f32f3d..121d709 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -48,7 +48,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
@@ -62,6 +61,7 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.rollback.IRollbackManager;
@@ -446,7 +446,7 @@
                 throw new IllegalArgumentException("Error: Can't open file: " + inPath);
             }
             try {
-                ApkLite baseApk = PackageParser.parseApkLite(fd.getFileDescriptor(), inPath, 0);
+                ApkLite baseApk = ApkLiteParseUtils.parseApkLite(fd.getFileDescriptor(), inPath, 0);
                 PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
                         null, null);
                 params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 4ea8a30..44928fb 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -18,8 +18,9 @@
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
 import android.service.pm.PackageProto;
 import android.util.proto.ProtoOutputStream;
 
@@ -31,9 +32,11 @@
 /**
  * Settings data for a particular package we know about.
  */
-public final class PackageSetting extends PackageSettingBase {
+public final class PackageSetting extends PackageSettingBase implements
+        ParsedPackage.PackageSettingCallback {
     int appId;
-    PackageParser.Package pkg;
+
+    public AndroidPackage pkg;
     /**
      * WARNING. The object reference is important. We perform integer equality and NOT
      * object equality to check whether shared user settings are the same.
@@ -50,12 +53,12 @@
     PackageSetting(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            long pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
-            List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
+            long pVersionCode, int pkgFlags, int privateFlags,
+            int sharedUserId, String[] usesStaticLibraries,
             long[] usesStaticLibrariesVersions) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
+                pVersionCode, pkgFlags, privateFlags,
                 usesStaticLibraries, usesStaticLibrariesVersions);
         this.sharedUserId = sharedUserId;
     }
@@ -116,10 +119,6 @@
                 : super.getPermissionsState();
     }
 
-    public PackageParser.Package getPackage() {
-        return pkg;
-    }
-
     public int getAppId() {
         return appId;
     }
@@ -132,6 +131,7 @@
         return installPermissionsFixed;
     }
 
+    // TODO(b/135203078): Remove these in favor of reading from the package directly
     public boolean isPrivileged() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
@@ -176,10 +176,6 @@
         return true;
     }
 
-    public boolean hasChildPackages() {
-        return childPackageNames != null && !childPackageNames.isEmpty();
-    }
-
     public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) {
         final long packageToken = proto.start(fieldId);
         proto.write(PackageProto.NAME, (realName != null ? realName : name));
@@ -190,18 +186,19 @@
         proto.write(PackageProto.INSTALLER_NAME, installerPackageName);
 
         if (pkg != null) {
-            proto.write(PackageProto.VERSION_STRING, pkg.mVersionName);
+            proto.write(PackageProto.VERSION_STRING, pkg.getVersionName());
 
             long splitToken = proto.start(PackageProto.SPLITS);
             proto.write(PackageProto.SplitProto.NAME, "base");
-            proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.baseRevisionCode);
+            proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.getBaseRevisionCode());
             proto.end(splitToken);
 
-            if (pkg.splitNames != null) {
-                for (int i = 0; i < pkg.splitNames.length; i++) {
+            if (pkg.getSplitNames() != null) {
+                for (int i = 0; i < pkg.getSplitNames().length; i++) {
                     splitToken = proto.start(PackageProto.SPLITS);
-                    proto.write(PackageProto.SplitProto.NAME, pkg.splitNames[i]);
-                    proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.splitRevisionCodes[i]);
+                    proto.write(PackageProto.SplitProto.NAME, pkg.getSplitNames()[i]);
+                    proto.write(PackageProto.SplitProto.REVISION_CODE,
+                            pkg.getSplitRevisionCodes()[i]);
                     proto.end(splitToken);
                 }
             }
@@ -218,4 +215,10 @@
         sharedUserId = other.sharedUserId;
         sharedUser = other.sharedUser;
     }
+
+    // TODO(b/135203078): Move to constructor
+    @Override
+    public void setAndroidPackage(AndroidPackage pkg) {
+        this.pkg = pkg;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 029673f..09c1789 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -36,7 +36,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.File;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
@@ -51,9 +50,6 @@
     public final String name;
     final String realName;
 
-    String parentPackageName;
-    List<String> childPackageNames;
-
     /**
      * Path where this package was found on disk. For monolithic packages
      * this is path to single base APK file; for cluster packages this is
@@ -137,14 +133,10 @@
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int pkgPrivateFlags,
-            String parentPackageName, List<String> childPackageNames,
             String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         super(pkgFlags, pkgPrivateFlags);
         this.name = name;
         this.realName = realName;
-        this.parentPackageName = parentPackageName;
-        this.childPackageNames = (childPackageNames != null)
-                ? new ArrayList<>(childPackageNames) : null;
         this.usesStaticLibraries = usesStaticLibraries;
         this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
         init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
@@ -232,8 +224,6 @@
     }
 
     private void doCopy(PackageSettingBase orig) {
-        childPackageNames = (orig.childPackageNames != null)
-                ? new ArrayList<>(orig.childPackageNames) : null;
         codePath = orig.codePath;
         codePathString = orig.codePathString;
         cpuAbiOverrideString = orig.cpuAbiOverrideString;
@@ -245,7 +235,6 @@
         lastUpdateTime = orig.lastUpdateTime;
         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
         // Intentionally skip mOldCodePaths; it's not relevant for copies
-        parentPackageName = orig.parentPackageName;
         primaryCpuAbiString = orig.primaryCpuAbiString;
         resourcePath = orig.resourcePath;
         resourcePathString = orig.resourcePathString;
@@ -657,8 +646,6 @@
 
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
-        this.parentPackageName = other.parentPackageName;
-        this.childPackageNames = other.childPackageNames;
         this.codePath = other.codePath;
         this.codePathString = other.codePathString;
         this.resourcePath = other.resourcePath;
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
index ac1f739..ce2c9e7 100644
--- a/services/core/java/com/android/server/pm/PackageUsage.java
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -20,7 +20,7 @@
 import static android.os.Process.SYSTEM_UID;
 
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.FileUtils;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -36,7 +36,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Map;
 
-class PackageUsage extends AbstractStatsBase<Map<String, PackageParser.Package>> {
+class PackageUsage extends AbstractStatsBase<Map<String, AndroidPackage>> {
 
     private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
     private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
@@ -52,7 +52,7 @@
     }
 
     @Override
-    protected void writeInternal(Map<String, PackageParser.Package> packages) {
+    protected void writeInternal(Map<String, AndroidPackage> packages) {
         AtomicFile file = getFile();
         FileOutputStream f = null;
         try {
@@ -66,13 +66,13 @@
             sb.append('\n');
             out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
 
-            for (PackageParser.Package pkg : packages.values()) {
+            for (AndroidPackage pkg : packages.values()) {
                 if (pkg.getLatestPackageUseTimeInMills() == 0L) {
                     continue;
                 }
                 sb.setLength(0);
-                sb.append(pkg.packageName);
-                for (long usageTimeInMillis : pkg.mLastPackageUsageTimeInMills) {
+                sb.append(pkg.getPackageName());
+                for (long usageTimeInMillis : pkg.getLastPackageUsageTimeInMills()) {
                     sb.append(' ');
                     sb.append(usageTimeInMillis);
                 }
@@ -90,7 +90,7 @@
     }
 
     @Override
-    protected void readInternal(Map<String, PackageParser.Package> packages) {
+    protected void readInternal(Map<String, AndroidPackage> packages) {
         AtomicFile file = getFile();
         BufferedInputStream in = null;
         try {
@@ -114,7 +114,7 @@
         }
     }
 
-    private void readVersion0LP(Map<String, PackageParser.Package> packages, InputStream in,
+    private void readVersion0LP(Map<String, AndroidPackage> packages, InputStream in,
             StringBuffer sb, String firstLine)
             throws IOException {
         // Initial version of the file had no version number and stored one
@@ -128,7 +128,7 @@
             }
 
             String packageName = tokens[0];
-            PackageParser.Package pkg = packages.get(packageName);
+            AndroidPackage pkg = packages.get(packageName);
             if (pkg == null) {
                 continue;
             }
@@ -137,12 +137,12 @@
             for (int reason = 0;
                     reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
                     reason++) {
-                pkg.mLastPackageUsageTimeInMills[reason] = timestamp;
+                pkg.mutate().setLastPackageUsageTimeInMills(reason, timestamp);
             }
         }
     }
 
-    private void readVersion1LP(Map<String, PackageParser.Package> packages, InputStream in,
+    private void readVersion1LP(Map<String, AndroidPackage> packages, InputStream in,
             StringBuffer sb) throws IOException {
         // Version 1 of the file started with the corresponding version
         // number and then stored a package name and eight timestamps per line.
@@ -154,7 +154,7 @@
             }
 
             String packageName = tokens[0];
-            PackageParser.Package pkg = packages.get(packageName);
+            AndroidPackage pkg = packages.get(packageName);
             if (pkg == null) {
                 continue;
             }
@@ -162,7 +162,8 @@
             for (int reason = 0;
                     reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
                     reason++) {
-                pkg.mLastPackageUsageTimeInMills[reason] = parseAsLong(tokens[reason + 1]);
+                pkg.mutate().setLastPackageUsageTimeInMills(reason,
+                        parseAsLong(tokens[reason + 1]));
             }
         }
     }
@@ -196,4 +197,4 @@
             sb.append((char)ch);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 4ff3e12..a506514 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -16,7 +16,10 @@
 
 package com.android.server.pm;
 
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
 import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.Process;
 import android.os.Trace;
 import android.util.DisplayMetrics;
@@ -30,8 +33,6 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
 /**
  * Helper class for parallel parsing of packages using {@link PackageParser}.
  * <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
@@ -65,14 +66,14 @@
 
     static class ParseResult {
 
-        PackageParser.Package pkg; // Parsed package
+        ParsedPackage parsedPackage; // Parsed package
         File scanFile; // File that was parsed
         Throwable throwable; // Set if an error occurs during parsing
 
         @Override
         public String toString() {
             return "ParseResult{" +
-                    "pkg=" + pkg +
+                    "parsedPackage=" + parsedPackage +
                     ", scanFile=" + scanFile +
                     ", throwable=" + throwable +
                     '}';
@@ -100,7 +101,7 @@
     /**
      * Submits the file for parsing
      * @param scanFile file to scan
-     * @param parseFlags parse falgs
+     * @param parseFlags parse flags
      */
     public void submit(File scanFile, int parseFlags) {
         mService.submit(() -> {
@@ -114,7 +115,7 @@
                 pp.setCacheDir(mCacheDir);
                 pp.setCallback(mPackageParserCallback);
                 pr.scanFile = scanFile;
-                pr.pkg = parsePackage(pp, scanFile, parseFlags);
+                pr.parsedPackage = parsePackage(pp, scanFile, parseFlags);
             } catch (Throwable e) {
                 pr.throwable = e;
             } finally {
@@ -133,9 +134,9 @@
     }
 
     @VisibleForTesting
-    protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
+    protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
             int parseFlags) throws PackageParser.PackageParserException {
-        return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
+        return packageParser.parseParsedPackage(scanFile, parseFlags, true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b94047e..c404dad 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -16,9 +16,9 @@
 
 package com.android.server.pm;
 
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Environment;
 import android.util.Slog;
 import android.util.Xml;
@@ -78,6 +78,13 @@
         sMacPermissions.add(new File(
             Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
 
+        // SystemExt mac permissions (optional).
+        final File systemExtMacPermission = new File(
+                Environment.getSystemExtDirectory(), "/etc/selinux/system_ext_mac_permissions.xml");
+        if (systemExtMacPermission.exists()) {
+            sMacPermissions.add(systemExtMacPermission);
+        }
+
         // Product mac permissions (optional).
         final File productMacPermission = new File(
                 Environment.getProductDirectory(), "/etc/selinux/product_mac_permissions.xml");
@@ -325,7 +332,7 @@
      *        MINIMUM_TARGETSDKVERSION.
      * @return String representing the resulting seinfo.
      */
-    public static String getSeInfo(PackageParser.Package pkg, boolean isPrivileged,
+    public static String getSeInfo(AndroidPackage pkg, boolean isPrivileged,
             int targetSdkVersion) {
         String seInfo = null;
         synchronized (sPolicies) {
@@ -354,8 +361,8 @@
         seInfo += TARGETSDKVERSION_STR + targetSdkVersion;
 
         if (DEBUG_POLICY_INSTALL) {
-            Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " +
-                    "seinfo=" + seInfo);
+            Slog.i(TAG, "package (" + pkg.getPackageName() + ") labeled with "
+                    + "seinfo=" + seInfo);
         }
         return seInfo;
     }
@@ -364,7 +371,7 @@
 /**
  * Holds valid policy representations of individual stanzas from a mac_permissions.xml
  * file. Each instance can further be used to assign seinfo values to apks using the
- * {@link Policy#getMatchedSeinfo} method. To create an instance of this use the
+ * {@link Policy#getMatchedSeInfo(AndroidPackage)} method. To create an instance of this use the
  * {@link PolicyBuilder} pattern class, where each instance is validated against a set
  * of invariants before being built and returned. Each instance can be guaranteed to
  * hold one valid policy stanza as outlined in the system/sepolicy/mac_permissions.xml
@@ -491,21 +498,21 @@
      * @return A string representing the seinfo matched during policy lookup.
      *         A value of null can also be returned if no match occured.
      */
-    public String getMatchedSeInfo(PackageParser.Package pkg) {
+    public String getMatchedSeInfo(AndroidPackage pkg) {
         // Check for exact signature matches across all certs.
         Signature[] certs = mCerts.toArray(new Signature[0]);
-        if (pkg.mSigningDetails != SigningDetails.UNKNOWN
-                && !Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) {
+        if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
+                && !Signature.areExactMatch(certs, pkg.getSigningDetails().signatures)) {
 
             // certs aren't exact match, but the package may have rotated from the known system cert
-            if (certs.length > 1 || !pkg.mSigningDetails.hasCertificate(certs[0])) {
+            if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
                 return null;
             }
         }
 
         // Check for inner package name matches given that the
         // signature checks already passed.
-        String seinfoValue = mPkgMap.get(pkg.packageName);
+        String seinfoValue = mPkgMap.get(pkg.getPackageName());
         if (seinfoValue != null) {
             return seinfoValue;
         }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4349ea7..7d6c482 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -42,7 +42,6 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
@@ -50,6 +49,10 @@
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -466,8 +469,8 @@
         final PackageSetting dp = mDisabledSysPackages.get(name);
         // always make sure the system package code and resource paths dont change
         if (dp == null && p.pkg != null && p.pkg.isSystem() && !p.pkg.isUpdatedSystemApp()) {
-            if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
-                p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+            if(p.pkg != null) {
+                p.pkg.mutate().setUpdatedSystemApp(true);
             }
             final PackageSetting disabled;
             if (replaced) {
@@ -493,14 +496,14 @@
             return null;
         }
         // Reset flag in ApplicationInfo object
-        if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
-            p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        if(p.pkg != null) {
+            p.pkg.mutate().setUpdatedSystemApp(false);
         }
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
-                p.parentPackageName, p.childPackageNames, p.usesStaticLibraries,
+                p.usesStaticLibraries,
                 p.usesStaticLibrariesVersions);
         mDisabledSysPackages.remove(name);
         return ret;
@@ -517,8 +520,7 @@
     PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
-            pkgFlags, int pkgPrivateFlags, String parentPackageName,
-            List<String> childPackageNames, String[] usesStaticLibraries,
+            pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
             long[] usesStaticLibraryNames) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
@@ -531,8 +533,8 @@
         }
         p = new PackageSetting(name, realName, codePath, resourcePath,
                 legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
-                childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
+                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags,
+                0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
         p.appId = uid;
         if (registerExistingAppIdLPw(uid, p, name)) {
             mPackages.put(name, p);
@@ -598,19 +600,15 @@
             File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, boolean instantApp,
-            boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
-            UserManagerService userManager,
+            boolean virtualPreload, UserManagerService userManager,
             String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                     + pkgName + " is adopting original package " + originalPkg.name);
             pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
-            pkgSetting.childPackageNames =
-                    (childPkgNames != null) ? new ArrayList<>(childPkgNames) : null;
             pkgSetting.codePath = codePath;
             pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
-            pkgSetting.parentPackageName = parentPkgName;
             pkgSetting.pkgFlags = pkgFlags;
             pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
             pkgSetting.primaryCpuAbiString = primaryCpuAbi;
@@ -628,7 +626,7 @@
             pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
                     null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
-                    parentPkgName, childPkgNames, 0 /*sharedUserId*/, usesStaticLibraries,
+                    0 /*sharedUserId*/, usesStaticLibraries,
                     usesStaticLibrariesVersions);
             pkgSetting.setTimeStamp(codePath.lastModified());
             pkgSetting.sharedUser = sharedUser;
@@ -715,7 +713,7 @@
             @NonNull File codePath, File resourcePath,
             @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi,
             @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags,
-            @Nullable List<String> childPkgNames, @NonNull UserManagerService userManager,
+            @NonNull UserManagerService userManager,
             @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions)
                     throws PackageManagerException {
         final String pkgName = pkgSetting.name;
@@ -793,9 +791,6 @@
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM;
         pkgSetting.primaryCpuAbiString = primaryCpuAbi;
         pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
-        if (childPkgNames != null) {
-            pkgSetting.childPackageNames = new ArrayList<>(childPkgNames);
-        }
         // Update static shared library dependencies if needed
         if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
                 && usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
@@ -864,15 +859,15 @@
 
     // TODO: Move to scanPackageOnlyLI() after verifying signatures are setup correctly
     // by that time.
-    void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
+    void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
         // Update signatures if needed.
         if (p.signatures.mSigningDetails.signatures == null) {
-            p.signatures.mSigningDetails = pkg.mSigningDetails;
+            p.signatures.mSigningDetails = pkg.getSigningDetails();
         }
         // If this app defines a shared user id initialize
         // the shared user signatures as well.
         if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
-            p.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
+            p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
         }
         addPackageSettingLPw(p, p.sharedUser);
     }
@@ -949,7 +944,7 @@
 
         int affectedUserId = UserHandle.USER_NULL;
         // Update permissions
-        for (String eachPerm : deletedPs.pkg.requestedPermissions) {
+        for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) {
             BasePermission bp = mPermissions.getPermission(eachPerm);
             if (bp == null) {
                 continue;
@@ -959,8 +954,8 @@
             boolean used = false;
             for (PackageSetting pkg : sus.packages) {
                 if (pkg.pkg != null
-                        && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName)
-                        && pkg.pkg.requestedPermissions.contains(eachPerm)) {
+                        && !pkg.pkg.getPackageName().equals(deletedPs.pkg.getPackageName())
+                        && pkg.pkg.getRequestedPermissions().contains(eachPerm)) {
                     used = true;
                     break;
                 }
@@ -970,13 +965,13 @@
             }
 
             PermissionsState permissionsState = sus.getPermissionsState();
-            PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.packageName);
+            PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.getPackageName());
 
             // If the package is shadowing is a disabled system package,
             // do not drop permissions that the shadowed package requests.
             if (disabledPs != null) {
                 boolean reqByDisabledSysPkg = false;
-                for (String permission : disabledPs.pkg.requestedPermissions) {
+                for (String permission : disabledPs.pkg.getRequestedPermissions()) {
                     if (permission.equals(eachPerm)) {
                         reqByDisabledSysPkg = true;
                         break;
@@ -2207,20 +2202,6 @@
         serializer.endTag(null, TAG_PERMISSIONS);
     }
 
-    void writeChildPackagesLPw(XmlSerializer serializer, List<String> childPackageNames)
-            throws IOException {
-        if (childPackageNames == null) {
-            return;
-        }
-        final int childCount = childPackageNames.size();
-        for (int i = 0; i < childCount; i++) {
-            String childPackageName = childPackageNames.get(i);
-            serializer.startTag(null, TAG_CHILD_PACKAGE);
-            serializer.attribute(null, ATTR_NAME, childPackageName);
-            serializer.endTag(null, TAG_CHILD_PACKAGE);
-        }
-    }
-
     void readUsesStaticLibLPw(XmlPullParser parser, PackageSetting outPs)
             throws IOException, XmlPullParserException {
         int outerDepth = parser.getDepth();
@@ -2674,17 +2655,15 @@
 
             StringBuilder sb = new StringBuilder();
             for (final PackageSetting pkg : mPackages.values()) {
-                if (pkg.pkg == null || pkg.pkg.applicationInfo == null
-                        || pkg.pkg.applicationInfo.dataDir == null) {
+                if (pkg.pkg == null || pkg.pkg.getDataDir() == null) {
                     if (!"android".equals(pkg.name)) {
                         Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
                     }
                     continue;
                 }
 
-                final ApplicationInfo ai = pkg.pkg.applicationInfo;
-                final String dataPath = ai.dataDir;
-                final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+                final String dataPath = pkg.pkg.getDataDir();
+                final boolean isDebug = (pkg.pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                 final int[] gids = pkg.getPermissionsState().computeGids(userIds);
 
                 // Avoid any application that has a space in its path.
@@ -2709,13 +2688,13 @@
                 //   system/core/libpackagelistparser
                 //
                 sb.setLength(0);
-                sb.append(ai.packageName);
+                sb.append(pkg.pkg.getPackageName());
                 sb.append(" ");
-                sb.append(ai.uid);
+                sb.append(pkg.pkg.getUid());
                 sb.append(isDebug ? " 1 " : " 0 ");
                 sb.append(dataPath);
                 sb.append(" ");
-                sb.append(ai.seInfo);
+                sb.append(pkg.pkg.getSeInfo());
                 sb.append(" ");
                 if (gids != null && gids.length > 0) {
                     sb.append(gids[0]);
@@ -2727,9 +2706,9 @@
                     sb.append("none");
                 }
                 sb.append(" ");
-                sb.append(ai.isProfileableByShell() ? "1" : "0");
+                sb.append(pkg.pkg.isProfileableByShell() ? "1" : "0");
                 sb.append(" ");
-                sb.append(String.valueOf(ai.longVersionCode));
+                sb.append(pkg.pkg.getLongVersionCode());
                 sb.append("\n");
                 writer.append(sb);
             }
@@ -2778,12 +2757,6 @@
             serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
         }
 
-        if (pkg.parentPackageName != null) {
-            serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
-        }
-
-        writeChildPackagesLPw(serializer, pkg.childPackageNames);
-
         writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
 
         // If this is a shared user, the permissions will be written there.
@@ -2847,15 +2820,10 @@
             serializer.attribute(null, "categoryHint",
                     Integer.toString(pkg.categoryHint));
         }
-        if (pkg.parentPackageName != null) {
-            serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
-        }
         if (pkg.updateAvailable) {
             serializer.attribute(null, "updateAvailable", "true");
         }
 
-        writeChildPackagesLPw(serializer, pkg.childPackageNames);
-
         writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
 
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
@@ -3148,13 +3116,13 @@
                 LocalServices.getService(PackageManagerInternal.class);
         for (PackageSetting ps : mPackages.values()) {
             if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
-                    && ps.pkg.preferredActivityFilters != null) {
-                ArrayList<PackageParser.ActivityIntentInfo> intents
-                        = ps.pkg.preferredActivityFilters;
+                    && ps.pkg.getPreferredActivityFilters() != null) {
+                List<ComponentParseUtils.ParsedActivityIntentInfo> intents
+                        = ps.pkg.getPreferredActivityFilters();
                 for (int i=0; i<intents.size(); i++) {
-                    PackageParser.ActivityIntentInfo aii = intents.get(i);
+                    ComponentParseUtils.ParsedActivityIntentInfo aii = intents.get(i);
                     applyDefaultPreferredActivityLPw(pmInternal, aii, new ComponentName(
-                                    ps.name, aii.activity.className), userId);
+                                    ps.name, aii.getClassName()), userId);
                 }
             }
         }
@@ -3513,7 +3481,7 @@
         PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
                 new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
                 secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
-                parentPackageName, null /*childPackageNames*/, 0 /*sharedUserId*/, null, null);
+                0 /*sharedUserId*/, null, null);
         String timeStampStr = parser.getAttributeValue(null, "ft");
         if (timeStampStr != null) {
             try {
@@ -3562,12 +3530,6 @@
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
                 readInstallPermissionsLPr(parser, ps.getPermissionsState());
-            } else if (parser.getName().equals(TAG_CHILD_PACKAGE)) {
-                String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
-                if (ps.childPackageNames == null) {
-                    ps.childPackageNames = new ArrayList<>();
-                }
-                ps.childPackageNames.add(childPackageName);
             } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
                 readUsesStaticLibLPw(parser, ps);
             } else {
@@ -3612,7 +3574,6 @@
         PackageSetting packageSetting = null;
         String version = null;
         long versionCode = 0;
-        String parentPackageName;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
@@ -3624,8 +3585,6 @@
 
             legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
 
-            parentPackageName = parser.getAttributeValue(null, "parentPackageName");
-
             legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
             primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
             secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
@@ -3752,7 +3711,7 @@
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                         new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
                         secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
-                        pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
+                        pkgPrivateFlags,
                         null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
@@ -3771,8 +3730,8 @@
                     packageSetting = new PackageSetting(name.intern(), realName, new File(
                             codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
-                            versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
-                            null /*childPackageNames*/, sharedUserId,
+                            versionCode, pkgFlags, pkgPrivateFlags,
+                            sharedUserId,
                             null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
                     packageSetting.setTimeStamp(timeStamp);
                     packageSetting.firstInstallTime = firstInstallTime;
@@ -3880,12 +3839,6 @@
                     packageSetting.keySetData.addDefinedKeySet(id, alias);
                 } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
                     readDomainVerificationLPw(parser, packageSetting);
-                } else if (tagName.equals(TAG_CHILD_PACKAGE)) {
-                    String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
-                    if (packageSetting.childPackageNames == null) {
-                        packageSetting.childPackageNames = new ArrayList<>();
-                    }
-                    packageSetting.childPackageNames.add(childPackageName);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <package>: " + parser.getName());
@@ -4038,13 +3991,13 @@
             Iterator<PackageSetting> packagesIterator = packages.iterator();
             for (int i = 0; i < packagesCount; i++) {
                 PackageSetting ps = packagesIterator.next();
-                if (ps.pkg == null || ps.pkg.applicationInfo == null) {
+                if (ps.pkg == null) {
                     continue;
                 }
                 final boolean shouldInstall = ps.isSystem() &&
                         (skipPackageWhitelist || installablePackages.contains(ps.name)) &&
                         !ArrayUtils.contains(disallowedPackages, ps.name) &&
-                        !ps.pkg.applicationInfo.hiddenUntilInstalled;
+                        !ps.pkg.isHiddenUntilInstalled();
                 // Only system apps are initially installed.
                 ps.setInstalled(shouldInstall, userHandle);
                 if (!shouldInstall) {
@@ -4055,8 +4008,8 @@
                 volumeUuids[i] = ps.volumeUuid;
                 names[i] = ps.name;
                 appIds[i] = ps.appId;
-                seinfos[i] = ps.pkg.applicationInfo.seInfo;
-                targetSdkVersions[i] = ps.pkg.applicationInfo.targetSdkVersion;
+                seinfos[i] = ps.pkg.getSeInfo();
+                targetSdkVersions[i] = ps.pkg.getTargetSdkVersion();
             }
         }
         t.traceBegin("createAppData");
@@ -4166,28 +4119,6 @@
         return mVerifierDeviceIdentity;
     }
 
-    boolean hasOtherDisabledSystemPkgWithChildLPr(String parentPackageName,
-            String childPackageName) {
-        final int packageCount = mDisabledSysPackages.size();
-        for (int i = 0; i < packageCount; i++) {
-            PackageSetting disabledPs = mDisabledSysPackages.valueAt(i);
-            if (disabledPs.childPackageNames == null || disabledPs.childPackageNames.isEmpty()) {
-                continue;
-            }
-            if (disabledPs.name.equals(parentPackageName)) {
-                continue;
-            }
-            final int childCount = disabledPs.childPackageNames.size();
-            for (int j = 0; j < childCount; j++) {
-                String currChildPackageName = disabledPs.childPackageNames.get(j);
-                if (currChildPackageName.equals(childPackageName)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     /**
      * Returns the disabled {@link PackageSetting} for the provided package name if one exists,
      * {@code null} otherwise.
@@ -4210,26 +4141,6 @@
         return getDisabledSystemPkgLPr(enabledPackageSetting.name);
     }
 
-    /**
-     * Fetches an array of the child {@link PackageSetting}s for all child package names referenced
-     * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced.
-     *
-     * Note: Any child packages not found will be null in the returned array.
-     */
-    @Nullable
-    public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) {
-        if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) {
-            return null;
-        }
-        final int childCount = parentPackageSetting.childPackageNames.size();
-        PackageSetting[] children =
-                new PackageSetting[childCount];
-        for (int i = 0; i < childCount; i++) {
-            children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i));
-        }
-        return children;
-    }
-
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
         final PackageSetting ps = mPackages.get(componentInfo.packageName);
         if (ps == null) return false;
@@ -4238,6 +4149,15 @@
         return userState.isMatch(componentInfo, flags);
     }
 
+    boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedComponent component, int flags,
+            int userId) {
+        final PackageSetting ps = mPackages.get(component.getPackageName());
+        if (ps == null) return false;
+
+        final PackageUserState userState = ps.readUserState(userId);
+        return userState.isMatch(pkg.isSystem(), pkg.isEnabled(), component, flags);
+    }
+
     String getInstallerPackageNameLPr(String packageName) {
         final PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
@@ -4455,6 +4375,7 @@
     void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
             ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf,
             Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
+        AndroidPackage pkg = ps.pkg;
         if (checkinTag != null) {
             pw.print(checkinTag);
             pw.print(",");
@@ -4470,15 +4391,16 @@
             pw.print(",");
             pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?");
             pw.println();
-            if (ps.pkg != null) {
+            if (pkg != null) {
                 pw.print(checkinTag); pw.print("-"); pw.print("splt,");
                 pw.print("base,");
-                pw.println(ps.pkg.baseRevisionCode);
-                if (ps.pkg.splitNames != null) {
-                    for (int i = 0; i < ps.pkg.splitNames.length; i++) {
+                pw.println(pkg.getBaseRevisionCode());
+                if (pkg.getSplitNames() != null) {
+                    int[] splitRevisionCodes = pkg.getSplitRevisionCodes();
+                    for (int i = 0; i < pkg.getSplitNames().length; i++) {
                         pw.print(checkinTag); pw.print("-"); pw.print("splt,");
-                        pw.print(ps.pkg.splitNames[i]); pw.print(",");
-                        pw.println(ps.pkg.splitRevisionCodes[i]);
+                        pw.print(pkg.getSplitNames()[i]); pw.print(",");
+                        pw.println(splitRevisionCodes[i]);
                     }
                 }
             }
@@ -4525,7 +4447,7 @@
         if (ps.sharedUser != null) {
             pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
         }
-        pw.print(prefix); pw.print("  pkg="); pw.println(ps.pkg);
+        pw.print(prefix); pw.print("  pkg="); pw.println(pkg);
         pw.print(prefix); pw.print("  codePath="); pw.println(ps.codePathString);
         if (permissionNames == null) {
             pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
@@ -4535,140 +4457,123 @@
             pw.print(prefix); pw.print("  secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
         }
         pw.print(prefix); pw.print("  versionCode="); pw.print(ps.versionCode);
-        if (ps.pkg != null) {
-            pw.print(" minSdk="); pw.print(ps.pkg.applicationInfo.minSdkVersion);
-            pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
+        if (pkg != null) {
+            pw.print(" minSdk="); pw.print(pkg.getMinSdkVersion());
+            pw.print(" targetSdk="); pw.print(pkg.getTargetSdkVersion());
         }
         pw.println();
-        if (ps.pkg != null) {
-            if (ps.pkg.parentPackage != null) {
-                PackageParser.Package parentPkg = ps.pkg.parentPackage;
-                PackageSetting pps = mPackages.get(parentPkg.packageName);
-                if (pps == null || !pps.codePathString.equals(parentPkg.codePath)) {
-                    pps = mDisabledSysPackages.get(parentPkg.packageName);
-                }
-                if (pps != null) {
-                    pw.print(prefix); pw.print("  parentPackage=");
-                    pw.println(pps.realName != null ? pps.realName : pps.name);
-                }
-            } else if (ps.pkg.childPackages != null) {
-                pw.print(prefix); pw.print("  childPackages=[");
-                final int childCount = ps.pkg.childPackages.size();
-                for (int i = 0; i < childCount; i++) {
-                    PackageParser.Package childPkg = ps.pkg.childPackages.get(i);
-                    PackageSetting cps = mPackages.get(childPkg.packageName);
-                    if (cps == null || !cps.codePathString.equals(childPkg.codePath)) {
-                        cps = mDisabledSysPackages.get(childPkg.packageName);
-                    }
-                    if (cps != null) {
-                        if (i > 0) {
-                            pw.print(", ");
-                        }
-                        pw.print(cps.realName != null ? cps.realName : cps.name);
-                    }
-                }
-                pw.println("]");
-            }
-            pw.print(prefix); pw.print("  versionName="); pw.println(ps.pkg.mVersionName);
-            pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, ps.pkg); pw.println();
-            final int apkSigningVersion = ps.pkg.mSigningDetails.signatureSchemeVersion;
+        if (pkg != null) {
+            pw.print(prefix); pw.print("  versionName="); pw.println(pkg.getVersionName());
+            pw.print(prefix); pw.print("  splits="); dumpSplitNames(pw, pkg); pw.println();
+            final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
             pw.print(prefix); pw.print("  apkSigningVersion="); pw.println(apkSigningVersion);
+            // TODO(b/135203078): Is there anything to print here with AppInfo removed?
             pw.print(prefix); pw.print("  applicationInfo=");
-                pw.println(ps.pkg.applicationInfo.toString());
-            pw.print(prefix); pw.print("  flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
+                pw.println(pkg.toAppInfo().toString());
+            pw.print(prefix); pw.print("  flags="); printFlags(pw, pkg.getFlags(),
                     FLAG_DUMP_SPEC); pw.println();
-            if (ps.pkg.applicationInfo.privateFlags != 0) {
+            if (pkg.getPrivateFlags() != 0) {
                 pw.print(prefix); pw.print("  privateFlags="); printFlags(pw,
-                        ps.pkg.applicationInfo.privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
+                        pkg.getPrivateFlags(), PRIVATE_FLAG_DUMP_SPEC); pw.println();
             }
-            pw.print(prefix); pw.print("  forceQueryable="); pw.println(ps.pkg.mForceQueryable);
-            if (ps.pkg.mQueriesPackages != null) {
-                pw.append(prefix).append("  queriesPackages=").println(ps.pkg.mQueriesPackages);
+            pw.print(prefix); pw.print("  forceQueryable="); pw.println(ps.pkg.isForceQueryable());
+            if (ps.pkg.getQueriesPackages() != null) {
+                pw.append(prefix).append("  queriesPackages=").println(ps.pkg.getQueriesPackages());
             }
-            if (ps.pkg.mQueriesIntents != null) {
-                pw.append(prefix).append("  queriesIntents=").println(ps.pkg.mQueriesIntents);
+            if (ps.pkg.getQueriesIntents() != null) {
+                pw.append(prefix).append("  queriesIntents=").println(ps.pkg.getQueriesIntents());
             }
-            pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
+            pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.getDataDir());
             pw.print(prefix); pw.print("  supportsScreens=[");
             boolean first = true;
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("small");
             }
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("medium");
             }
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("large");
             }
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("xlarge");
             }
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("resizeable");
             }
-            if ((ps.pkg.applicationInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+            if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
                 if (!first)
                     pw.print(", ");
                 first = false;
                 pw.print("anyDensity");
             }
             pw.println("]");
-            if (ps.pkg.libraryNames != null && ps.pkg.libraryNames.size() > 0) {
+            List<String> libraryNames = pkg.getLibraryNames();
+            if (libraryNames != null && libraryNames.size() > 0) {
                 pw.print(prefix); pw.println("  dynamic libraries:");
-                for (int i = 0; i<ps.pkg.libraryNames.size(); i++) {
+                for (int i = 0; i< libraryNames.size(); i++) {
                     pw.print(prefix); pw.print("    ");
-                            pw.println(ps.pkg.libraryNames.get(i));
+                            pw.println(libraryNames.get(i));
                 }
             }
-            if (ps.pkg.staticSharedLibName != null) {
+            if (pkg.getStaticSharedLibName() != null) {
                 pw.print(prefix); pw.println("  static library:");
                 pw.print(prefix); pw.print("    ");
-                pw.print("name:"); pw.print(ps.pkg.staticSharedLibName);
-                pw.print(" version:"); pw.println(ps.pkg.staticSharedLibVersion);
+                pw.print("name:"); pw.print(pkg.getStaticSharedLibName());
+                pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
             }
-            if (ps.pkg.usesLibraries != null && ps.pkg.usesLibraries.size() > 0) {
+
+            List<String> usesLibraries = pkg.getUsesLibraries();
+            if (usesLibraries != null && usesLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesLibraries:");
-                for (int i=0; i<ps.pkg.usesLibraries.size(); i++) {
-                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.usesLibraries.get(i));
+                for (int i=0; i< usesLibraries.size(); i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(usesLibraries.get(i));
                 }
             }
-            if (ps.pkg.usesStaticLibraries != null
-                    && ps.pkg.usesStaticLibraries.size() > 0) {
+
+            List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+            long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
+            if (usesStaticLibraries != null
+                    && usesStaticLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesStaticLibraries:");
-                for (int i=0; i<ps.pkg.usesStaticLibraries.size(); i++) {
+                for (int i=0; i< usesStaticLibraries.size(); i++) {
                     pw.print(prefix); pw.print("    ");
-                    pw.print(ps.pkg.usesStaticLibraries.get(i)); pw.print(" version:");
-                            pw.println(ps.pkg.usesStaticLibrariesVersions[i]);
+                    pw.print(usesStaticLibraries.get(i)); pw.print(" version:");
+                            pw.println(usesStaticLibrariesVersions[i]);
                 }
             }
-            if (ps.pkg.usesOptionalLibraries != null
-                    && ps.pkg.usesOptionalLibraries.size() > 0) {
+
+            List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
+            if (usesOptionalLibraries != null
+                    && usesOptionalLibraries.size() > 0) {
                 pw.print(prefix); pw.println("  usesOptionalLibraries:");
-                for (int i=0; i<ps.pkg.usesOptionalLibraries.size(); i++) {
+                for (int i=0; i< usesOptionalLibraries.size(); i++) {
                     pw.print(prefix); pw.print("    ");
-                    pw.println(ps.pkg.usesOptionalLibraries.get(i));
+                    pw.println(usesOptionalLibraries.get(i));
                 }
             }
-            if (ps.pkg.usesLibraryFiles != null
-                    && ps.pkg.usesLibraryFiles.length > 0) {
+
+            String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
+            if (usesLibraryFiles != null
+                    && usesLibraryFiles.length > 0) {
                 pw.print(prefix); pw.println("  usesLibraryFiles:");
-                for (int i=0; i<ps.pkg.usesLibraryFiles.length; i++) {
-                    pw.print(prefix); pw.print("    "); pw.println(ps.pkg.usesLibraryFiles[i]);
+                for (int i=0; i< usesLibraryFiles.length; i++) {
+                    pw.print(prefix); pw.print("    "); pw.println(usesLibraryFiles[i]);
                 }
             }
         }
@@ -4696,40 +4601,40 @@
         pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
                 pw.println();
 
-        if (ps.pkg != null && ps.pkg.mOverlayTarget != null) {
-            pw.print(prefix); pw.print("  overlayTarget="); pw.println(ps.pkg.mOverlayTarget);
-            pw.print(prefix); pw.print("  overlayCategory="); pw.println(ps.pkg.mOverlayCategory);
+        if (pkg != null && pkg.getOverlayTarget() != null) {
+            pw.print(prefix); pw.print("  overlayTarget="); pw.println(pkg.getOverlayTarget());
+            pw.print(prefix); pw.print("  overlayCategory="); pw.println(pkg.getOverlayCategory());
         }
 
-        if (ps.pkg != null && ps.pkg.permissions != null && ps.pkg.permissions.size() > 0) {
-            final ArrayList<PackageParser.Permission> perms = ps.pkg.permissions;
+        if (pkg != null && pkg.getPermissions() != null && pkg.getPermissions().size() > 0) {
+            final List<ParsedPermission> perms = pkg.getPermissions();
             pw.print(prefix); pw.println("  declared permissions:");
             for (int i=0; i<perms.size(); i++) {
-                PackageParser.Permission perm = perms.get(i);
+                ParsedPermission perm = perms.get(i);
                 if (permissionNames != null
-                        && !permissionNames.contains(perm.info.name)) {
+                        && !permissionNames.contains(perm.getName())) {
                     continue;
                 }
-                pw.print(prefix); pw.print("    "); pw.print(perm.info.name);
+                pw.print(prefix); pw.print("    "); pw.print(perm.getName());
                 pw.print(": prot=");
-                pw.print(PermissionInfo.protectionToString(perm.info.protectionLevel));
-                if ((perm.info.flags&PermissionInfo.FLAG_COSTS_MONEY) != 0) {
+                pw.print(PermissionInfo.protectionToString(perm.protectionLevel));
+                if ((perm.flags&PermissionInfo.FLAG_COSTS_MONEY) != 0) {
                     pw.print(", COSTS_MONEY");
                 }
-                if ((perm.info.flags&PermissionInfo.FLAG_REMOVED) != 0) {
+                if ((perm.flags&PermissionInfo.FLAG_REMOVED) != 0) {
                     pw.print(", HIDDEN");
                 }
-                if ((perm.info.flags&PermissionInfo.FLAG_INSTALLED) != 0) {
+                if ((perm.flags&PermissionInfo.FLAG_INSTALLED) != 0) {
                     pw.print(", INSTALLED");
                 }
                 pw.println();
             }
         }
 
-        if ((permissionNames != null || dumpAll) && ps.pkg != null
-                && ps.pkg.requestedPermissions != null
-                && ps.pkg.requestedPermissions.size() > 0) {
-            final ArrayList<String> perms = ps.pkg.requestedPermissions;
+        if ((permissionNames != null || dumpAll) && pkg != null
+                && pkg.getRequestedPermissions() != null
+                && pkg.getRequestedPermissions().size() > 0) {
+            final List<String> perms = pkg.getRequestedPermissions();
             pw.print(prefix); pw.println("  requested permissions:");
             for (int i=0; i<perms.size(); i++) {
                 String perm = perms.get(i);
@@ -4996,22 +4901,24 @@
         pw.print(mReadMessages.toString());
     }
 
-    private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
+    private static void dumpSplitNames(PrintWriter pw, AndroidPackage pkg) {
         if (pkg == null) {
             pw.print("unknown");
         } else {
             // [base:10, config.mdpi, config.xhdpi:12]
             pw.print("[");
             pw.print("base");
-            if (pkg.baseRevisionCode != 0) {
-                pw.print(":"); pw.print(pkg.baseRevisionCode);
+            if (pkg.getBaseRevisionCode() != 0) {
+                pw.print(":"); pw.print(pkg.getBaseRevisionCode());
             }
-            if (pkg.splitNames != null) {
-                for (int i = 0; i < pkg.splitNames.length; i++) {
+            String[] splitNames = pkg.getSplitNames();
+            int[] splitRevisionCodes = pkg.getSplitRevisionCodes();
+            if (splitNames != null) {
+                for (int i = 0; i < splitNames.length; i++) {
                     pw.print(", ");
-                    pw.print(pkg.splitNames[i]);
-                    if (pkg.splitRevisionCodes[i] != 0) {
-                        pw.print(":"); pw.print(pkg.splitRevisionCodes[i]);
+                    pw.print(splitNames[i]);
+                    if (splitRevisionCodes[i] != 0) {
+                        pw.print(":"); pw.print(splitRevisionCodes[i]);
                     }
                 }
             }
@@ -5087,22 +4994,23 @@
     }
 
     void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps) {
-        dumpComponents(pw, prefix, ps, "activities:", ps.pkg.activities);
-        dumpComponents(pw, prefix, ps, "services:", ps.pkg.services);
-        dumpComponents(pw, prefix, ps, "receivers:", ps.pkg.receivers);
-        dumpComponents(pw, prefix, ps, "providers:", ps.pkg.providers);
-        dumpComponents(pw, prefix, ps, "instrumentations:", ps.pkg.instrumentation);
+        // TODO(b/135203078): ParsedComponent toString methods for dumping
+        dumpComponents(pw, prefix, "activities:", ps.pkg.getActivities());
+        dumpComponents(pw, prefix, "services:", ps.pkg.getServices());
+        dumpComponents(pw, prefix, "receivers:", ps.pkg.getReceivers());
+        dumpComponents(pw, prefix, "providers:", ps.pkg.getProviders());
+        dumpComponents(pw, prefix, "instrumentations:", ps.pkg.getInstrumentations());
     }
 
-    void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps,
-            String label, List<? extends PackageParser.Component<?>> list) {
+    void dumpComponents(PrintWriter pw, String prefix, String label,
+            List<? extends ParsedComponent> list) {
         final int size = CollectionUtils.size(list);
         if (size == 0) {
             return;
         }
         pw.print(prefix);pw.println(label);
         for (int i = 0; i < size; i++) {
-            final PackageParser.Component<?> component = list.get(i);
+            final ParsedComponent component = list.get(i);
             pw.print(prefix);pw.print("  ");
             pw.println(component.getComponentName().flattenToShortString());
         }
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 2ee07a2..cff1944 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.service.pm.PackageServiceDumpProto;
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
@@ -46,7 +46,7 @@
     // that all apps within the sharedUser run in the same selinux context.
     int seInfoTargetSdkVersion;
 
-    final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();
+    final ArraySet<PackageSetting> packages = new ArraySet<>();
 
     final PackageSignatures signatures = new PackageSignatures();
     Boolean signaturesChanged;
@@ -98,7 +98,7 @@
         // If this is the first package added to this shared user, temporarily (until next boot) use
         // its targetSdkVersion when assigning seInfo for the shared user.
         if ((packages.size() == 0) && (packageSetting.pkg != null)) {
-            seInfoTargetSdkVersion = packageSetting.pkg.applicationInfo.targetSdkVersion;
+            seInfoTargetSdkVersion = packageSetting.pkg.getTargetSdkVersion();
         }
         if (packages.add(packageSetting)) {
             setFlags(this.pkgFlags | packageSetting.pkgFlags);
@@ -106,11 +106,11 @@
         }
     }
 
-    public @Nullable List<PackageParser.Package> getPackages() {
+    public @Nullable List<AndroidPackage> getPackages() {
         if (packages == null || packages.size() == 0) {
             return null;
         }
-        final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size());
+        final ArrayList<AndroidPackage> pkgList = new ArrayList<>(packages.size());
         for (PackageSetting ps : packages) {
             if ((ps == null) || (ps.pkg == null)) {
                 continue;
@@ -131,20 +131,20 @@
      * restrictive selinux domain.
      */
     public void fixSeInfoLocked() {
-        final List<PackageParser.Package> pkgList = getPackages();
+        final List<AndroidPackage> pkgList = getPackages();
         if (pkgList == null || pkgList.size() == 0) {
             return;
         }
 
-        for (PackageParser.Package pkg : pkgList) {
-            if (pkg.applicationInfo.targetSdkVersion < seInfoTargetSdkVersion) {
-                seInfoTargetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+        for (AndroidPackage pkg : pkgList) {
+            if (pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) {
+                seInfoTargetSdkVersion = pkg.getTargetSdkVersion();
             }
         }
-        for (PackageParser.Package pkg : pkgList) {
+        for (AndroidPackage pkg : pkgList) {
             final boolean isPrivileged = isPrivileged() | pkg.isPrivileged();
-            pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
-                seInfoTargetSdkVersion);
+            pkg.mutate().setSeInfo(SELinuxMMAC.getSeInfo(pkg, isPrivileged,
+                    seInfoTargetSdkVersion));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5f86708..dace598 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3979,6 +3979,13 @@
         }
 
         @Override
+        public boolean isDeviceManaged() {
+            synchronized (mUsersLock) {
+                return mIsDeviceManaged;
+            }
+        }
+
+        @Override
         public void setUserManaged(@UserIdInt int userId, boolean isManaged) {
             synchronized (mUsersLock) {
                 mIsUserManaged.put(userId, isManaged);
@@ -3986,6 +3993,13 @@
         }
 
         @Override
+        public boolean isUserManaged(@UserIdInt int userId) {
+            synchronized (mUsersLock) {
+                return mIsUserManaged.get(userId);
+            }
+        }
+
+        @Override
         public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
             long ident = Binder.clearCallingIdentity();
             try {
@@ -4205,6 +4219,7 @@
             return restrictions != null && restrictions.getBoolean(restrictionKey);
         }
 
+        @Override
         public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
             UserData userData;
             synchronized (mUsersLock) {
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 036d1e8..793ea47 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.content.res.Resources;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -151,15 +152,15 @@
                     return;
                 }
                 final boolean install =
-                        (userWhitelist == null || userWhitelist.contains(pkg.packageName))
-                        && !pkg.applicationInfo.hiddenUntilInstalled;
+                        (userWhitelist == null || userWhitelist.contains(pkg.getPackageName()))
+                        && !pkg.isHiddenUntilInstalled();
                 if (isUpgrade && !isFirstBoot && !install) {
                     return; // To be careful, we don’t uninstall apps during OTAs
                 }
                 final boolean changed = pmInt.setInstalled(pkg, userId, install);
                 if (changed) {
                     Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
-                            + pkg.packageName + " for user " + userId);
+                            + pkg.getPackageName() + " for user " + userId);
                 }
             });
         }
@@ -180,7 +181,7 @@
 
         // Check whether all whitelisted packages are indeed on the system.
         for (String pkgName : allWhitelistedPackages) {
-            PackageParser.Package pkg = pmInt.getPackage(pkgName);
+            AndroidPackage pkg = pmInt.getPackage(pkgName);
             if (pkg == null) {
                 Slog.w(TAG, pkgName + " is whitelisted but not present.");
             } else if (!pkg.isSystem()) {
@@ -194,8 +195,8 @@
         }
         final boolean doWtf = isEnforceMode(mode);
         pmInt.forEachPackage(pkg -> {
-            if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.manifestPackageName)) {
-                final String msg = "System package " + pkg.manifestPackageName
+            if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.getManifestPackageName())) {
+                final String msg = "System package " + pkg.getManifestPackageName()
                         + " is not whitelisted using 'install-in-user-type' in SystemConfig "
                         + "for any user types!";
                 if (doWtf) {
@@ -286,7 +287,7 @@
             if (shouldInstallPackage(pkg, mWhitelitsedPackagesForUserTypes,
                     whitelistedPackages, isImplicitWhitelistMode, isSystemUser)) {
                 // Although the whitelist uses manifest names, this function returns packageNames.
-                installPackages.add(pkg.packageName);
+                installPackages.add(pkg.getPackageName());
             }
         });
         return installPackages;
@@ -307,12 +308,12 @@
      * @param isSystemUser whether the user is USER_SYSTEM (which gets special treatment).
      */
     @VisibleForTesting
-    static boolean shouldInstallPackage(PackageParser.Package sysPkg,
+    static boolean shouldInstallPackage(AndroidPackage sysPkg,
             @NonNull ArrayMap<String, Integer> userTypeWhitelist,
             @NonNull Set<String> userWhitelist, boolean isImplicitWhitelistMode,
             boolean isSystemUser) {
 
-        final String pkgName = sysPkg.manifestPackageName;
+        final String pkgName = sysPkg.getManifestPackageName();
         boolean install = (isImplicitWhitelistMode && !userTypeWhitelist.containsKey(pkgName))
                 || userWhitelist.contains(pkgName);
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index b3f1867..661497c 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -25,13 +25,13 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.ArtManager.ProfileType;
 import android.content.pm.dex.ArtManagerInternal;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.content.pm.dex.PackageOptimizationInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -386,9 +386,10 @@
      *   - create the current primary profile to save time at app startup time.
      *   - copy the profiles from the associated dex metadata file to the reference profile.
      */
-    public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
+    public void prepareAppProfiles(
+            AndroidPackage pkg, @UserIdInt int user,
             boolean updateReferenceProfileContent) {
-        final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+        final int appId = UserHandle.getAppId(pkg.getUid());
         if (user < 0) {
             Slog.wtf(TAG, "Invalid user id: " + user);
             return;
@@ -411,23 +412,24 @@
                     dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
                 }
                 synchronized (mInstaller) {
-                    boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
+                    boolean result = mInstaller.prepareAppProfile(pkg.getPackageName(), user, appId,
                             profileName, codePath, dexMetadataPath);
                     if (!result) {
                         Slog.e(TAG, "Failed to prepare profile for " +
-                                pkg.packageName + ":" + codePath);
+                                pkg.getPackageName() + ":" + codePath);
                     }
                 }
             }
         } catch (InstallerException e) {
-            Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
+            Slog.e(TAG, "Failed to prepare profile for " + pkg.getPackageName(), e);
         }
     }
 
     /**
      * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
      */
-    public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
+    public void prepareAppProfiles(
+            AndroidPackage pkg, int[] user,
             boolean updateReferenceProfileContent) {
         for (int i = 0; i < user.length; i++) {
             prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
@@ -437,12 +439,12 @@
     /**
      * Clear the profiles for the given package.
      */
-    public void clearAppProfiles(PackageParser.Package pkg) {
+    public void clearAppProfiles(AndroidPackage pkg) {
         try {
             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
             for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
                 String profileName = packageProfileNames.valueAt(i);
-                mInstaller.clearAppProfiles(pkg.packageName, profileName);
+                mInstaller.clearAppProfiles(pkg.getPackageName(), profileName);
             }
         } catch (InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
@@ -452,15 +454,15 @@
     /**
      * Dumps the profiles for the given package.
      */
-    public void dumpProfiles(PackageParser.Package pkg) {
-        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+    public void dumpProfiles(AndroidPackage pkg) {
+        final int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
         try {
             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
             for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
                 String codePath = packageProfileNames.keyAt(i);
                 String profileName = packageProfileNames.valueAt(i);
                 synchronized (mInstallLock) {
-                    mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
+                    mInstaller.dumpProfiles(sharedGid, pkg.getPackageName(), profileName, codePath);
                 }
             }
         } catch (InstallerException e) {
@@ -471,14 +473,13 @@
     /**
      * Compile layout resources in a given package.
      */
-    public boolean compileLayouts(PackageParser.Package pkg) {
+    public boolean compileLayouts(AndroidPackage pkg) {
         try {
-            final String packageName = pkg.packageName;
-            final String apkPath = pkg.baseCodePath;
-            final ApplicationInfo appInfo = pkg.applicationInfo;
-            final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
-            if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()
-                    || appInfo.isDefaultToDeviceProtectedStorage()) {
+            final String packageName = pkg.getPackageName();
+            final String apkPath = pkg.getBaseCodePath();
+            final String outDexFile = pkg.getDataDir() + "/code_cache/compiled_view.dex";
+            if (pkg.isPrivileged() || pkg.isEmbeddedDexUsed()
+                    || pkg.isDefaultToDeviceProtectedStorage()) {
                 // Privileged apps prefer to load trusted code so they don't use compiled views.
                 // If the app is not privileged but prefers code integrity, also avoid compiling
                 // views.
@@ -492,7 +493,7 @@
             try {
                 synchronized (mInstallLock) {
                     return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
-                            appInfo.uid);
+                            pkg.getUid());
                 }
             } finally {
                 Binder.restoreCallingIdentity(callingId);
@@ -508,15 +509,19 @@
      * Build the profiles names for all the package code paths (excluding resource only paths).
      * Return the map [code path -> profile name].
      */
-    private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
+    private ArrayMap<String, String> getPackageProfileNames(AndroidPackage pkg) {
         ArrayMap<String, String> result = new ArrayMap<>();
-        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
+        if ((pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            result.put(pkg.getBaseCodePath(), ArtManager.getProfileName(null));
         }
-        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-            for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
-                    result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
+
+        String[] splitCodePaths = pkg.getSplitCodePaths();
+        int[] splitFlags = pkg.getSplitFlags();
+        String[] splitNames = pkg.getSplitNames();
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            for (int i = 0; i < splitCodePaths.length; i++) {
+                if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+                    result.put(splitCodePaths[i], ArtManager.getProfileName(splitNames[i]));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index f56231f..5df5380 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -595,7 +595,7 @@
 
         // We found the package. Now record the usage for all declared ISAs.
         boolean update = false;
-        for (String isa : getAppDexInstructionSets(info)) {
+        for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) {
             boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
                     dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
                     searchResult.mOwningPackageName,
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 5a473c1..6e6b137 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -18,10 +18,12 @@
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
 import java.util.List;
@@ -66,7 +68,7 @@
      * {@link android.app.LoadedApk#makePaths(
      * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
      */
-    public static String[] getClassLoaderContexts(ApplicationInfo info,
+    public static String[] getClassLoaderContexts(AndroidPackage pkg,
             List<SharedLibraryInfo> sharedLibraries, boolean[] pathsWithCode) {
         // The base class loader context contains only the shared library.
         String sharedLibrariesContext = "";
@@ -75,8 +77,8 @@
         }
 
         String baseApkContextClassLoader = encodeClassLoader(
-                "", info.classLoaderName, sharedLibrariesContext);
-        if (info.getSplitCodePaths() == null) {
+                "", pkg.getAppInfoClassLoaderName(), sharedLibrariesContext);
+        if (pkg.getSplitCodePaths() == null) {
             // The application has no splits.
             return new String[] {baseApkContextClassLoader};
         }
@@ -84,11 +86,11 @@
         // The application has splits. Compute their class loader contexts.
 
         // First, cache the relative paths of the splits and do some sanity checks
-        String[] splitRelativeCodePaths = getSplitRelativeCodePaths(info);
+        String[] splitRelativeCodePaths = getSplitRelativeCodePaths(pkg);
 
         // The splits have an implicit dependency on the base apk.
         // This means that we have to add the base apk file in addition to the shared libraries.
-        String baseApkName = new File(info.getBaseCodePath()).getName();
+        String baseApkName = new File(pkg.getBaseCodePath()).getName();
         String baseClassPath = baseApkName;
 
         // The result is stored in classLoaderContexts.
@@ -97,7 +99,11 @@
         String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
         classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
 
-        if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) {
+        SparseArray<int[]> splitDependencies = pkg.getSplitDependencies();
+
+        if (!pkg.requestsIsolatedSplitLoading()
+                || splitDependencies == null
+                || splitDependencies.size() == 0) {
             // If the app didn't request for the splits to be loaded in isolation or if it does not
             // declare inter-split dependencies, then all the splits will be loaded in the base
             // apk class loader (in the order of their definition).
@@ -105,7 +111,7 @@
             for (int i = 1; i < classLoaderContexts.length; i++) {
                 if (pathsWithCode[i]) {
                     classLoaderContexts[i] = encodeClassLoader(
-                            classpath, info.classLoaderName, sharedLibrariesContext);
+                            classpath, pkg.getAppInfoClassLoaderName(), sharedLibrariesContext);
                 } else {
                     classLoaderContexts[i] = null;
                 }
@@ -132,11 +138,10 @@
             String[] splitClassLoaderEncodingCache = new String[splitRelativeCodePaths.length];
             for (int i = 0; i < splitRelativeCodePaths.length; i++) {
                 splitClassLoaderEncodingCache[i] = encodeClassLoader(splitRelativeCodePaths[i],
-                        info.splitClassLoaderNames[i]);
+                        pkg.getSplitClassLoaderNames()[i]);
             }
             String splitDependencyOnBase = encodeClassLoader(
-                    baseClassPath, info.classLoaderName);
-            SparseArray<int[]> splitDependencies = info.splitDependencies;
+                    baseClassPath, pkg.getClassLoaderName());
 
             // Note that not all splits have dependencies (e.g. configuration splits)
             // The splits without dependencies will have classLoaderContexts[config_split_index]
@@ -154,7 +159,8 @@
             // We also need to add the class loader of the current split which should
             // come first in the context.
             for (int i = 1; i < classLoaderContexts.length; i++) {
-                String splitClassLoader = encodeClassLoader("", info.splitClassLoaderNames[i - 1]);
+                String splitClassLoader = encodeClassLoader("",
+                        pkg.getSplitClassLoaderNames()[i - 1]);
                 if (pathsWithCode[i]) {
                     // If classLoaderContexts[i] is null it means that the split does not have
                     // any dependency. In this case its context equals its declared class loader.
@@ -394,11 +400,11 @@
      * Returns the relative paths of the splits declared by the application {@code info}.
      * Assumes that the application declares a non-null array of splits.
      */
-    private static String[] getSplitRelativeCodePaths(ApplicationInfo info) {
-        String baseCodePath = new File(info.getBaseCodePath()).getParent();
-        String[] splitCodePaths = info.getSplitCodePaths();
-        String[] splitRelativeCodePaths = new String[splitCodePaths.length];
-        for (int i = 0; i < splitCodePaths.length; i++) {
+    private static String[] getSplitRelativeCodePaths(AndroidPackage pkg) {
+        String baseCodePath = new File(pkg.getBaseCodePath()).getParent();
+        String[] splitCodePaths = pkg.getSplitCodePaths();
+        String[] splitRelativeCodePaths = new String[ArrayUtils.size(splitCodePaths)];
+        for (int i = 0; i < splitRelativeCodePaths.length; i++) {
             File pathFile = new File(splitCodePaths[i]);
             splitRelativeCodePaths[i] = pathFile.getName();
             // Sanity check that the base paths of the splits are all the same.
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index 8d8e17e..b7443f3 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -16,10 +16,10 @@
 
 package com.android.server.pm.dex;
 
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Binder;
 import android.util.Log;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.pm.Installer;
 
@@ -33,19 +33,18 @@
         mInstaller = installer;
     }
 
-    public boolean compileLayouts(PackageParser.Package pkg) {
+    public boolean compileLayouts(AndroidPackage pkg) {
         try {
-            final String packageName = pkg.packageName;
-            final String apkPath = pkg.baseCodePath;
-            final ApplicationInfo appInfo = pkg.applicationInfo;
-            final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
+            final String packageName = pkg.getPackageName();
+            final String apkPath = pkg.getBaseCodePath();
+            final String outDexFile = pkg.getDataDir() + "/code_cache/compiled_view.dex";
             Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
                 ") to " + outDexFile);
             long callingId = Binder.clearCallingIdentity();
             try {
                 synchronized (mInstallLock) {
                     return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
-                        appInfo.uid);
+                        pkg.getUid());
                 }
             } finally {
                 Binder.restoreCallingIdentity(callingId);
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 6d22faa..29248b5 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -29,10 +29,12 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Permission;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.Signature;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.PackageInfoUtils;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
@@ -89,7 +91,7 @@
 
     int protectionLevel;
 
-    PackageParser.Permission perm;
+    ParsedPermission perm;
 
     PermissionInfo pendingPermissionInfo;
 
@@ -113,12 +115,6 @@
         protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
     }
 
-    @Override
-    public String toString() {
-        return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
-                + "}";
-    }
-
     public String getName() {
         return name;
     }
@@ -144,7 +140,7 @@
         this.gids = gids;
         this.perUser = perUser;
     }
-    public void setPermission(@Nullable Permission perm) {
+    public void setPermission(@Nullable ParsedPermission perm) {
         this.perm = perm;
     }
     public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) {
@@ -165,13 +161,13 @@
 
     public int calculateFootprint(BasePermission perm) {
         if (uid == perm.uid) {
-            return perm.name.length() + perm.perm.info.calculateFootprint();
+            return perm.name.length() + perm.perm.calculateFootprint();
         }
         return 0;
     }
 
-    public boolean isPermission(Permission perm) {
-        return this.perm == perm;
+    public boolean isPermission(ParsedPermission perm) {
+        return Objects.equals(this.perm.className, perm.className);
     }
 
     public boolean isDynamic() {
@@ -189,29 +185,24 @@
     }
 
     public boolean isRemoved() {
-        return perm != null && perm.info != null
-                && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0;
+        return perm != null && (perm.flags & PermissionInfo.FLAG_REMOVED) != 0;
     }
 
     public boolean isSoftRestricted() {
-        return perm != null && perm.info != null
-                && (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+        return perm != null && (perm.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
     }
 
     public boolean isHardRestricted() {
-        return perm != null && perm.info != null
-                && (perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+        return perm != null && (perm.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
     }
 
     public boolean isHardOrSoftRestricted() {
-        return perm != null && perm.info != null
-                && (perm.info.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
+        return perm != null && (perm.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
                 | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
     }
 
     public boolean isImmutablyRestricted() {
-        return perm != null && perm.info != null
-                && (perm.info.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+        return perm != null && (perm.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
     }
 
     public boolean isSignature() {
@@ -297,13 +288,12 @@
                 (this.protectionLevel != protectionLevel
                     || perm == null
                     || uid != tree.uid
-                    || !perm.owner.equals(tree.perm.owner)
-                    || !comparePermissionInfos(perm.info, info));
+                    || !Objects.equals(perm.getPackageName(), tree.perm.getPackageName())
+                    || !comparePermissionInfos(perm, info));
         this.protectionLevel = protectionLevel;
         info = new PermissionInfo(info);
         info.protectionLevel = protectionLevel;
-        perm = new PackageParser.Permission(tree.perm.owner, info);
-        perm.info.packageName = tree.perm.info.packageName;
+        perm = new ParsedPermission(tree.perm);
         uid = tree.uid;
         return changed;
     }
@@ -316,71 +306,89 @@
             final BasePermission tree = findPermissionTree(permissionTrees, name);
             if (tree != null && tree.perm != null) {
                 sourcePackageSetting = tree.sourcePackageSetting;
-                perm = new PackageParser.Permission(tree.perm.owner,
-                        new PermissionInfo(pendingPermissionInfo));
-                perm.info.packageName = tree.perm.info.packageName;
-                perm.info.name = name;
+                perm = new ParsedPermission(tree.perm);
+                perm.protectionLevel = pendingPermissionInfo.protectionLevel;
+                perm.flags = pendingPermissionInfo.flags;
+                perm.setGroup(pendingPermissionInfo.group);
+                perm.backgroundPermission = pendingPermissionInfo.backgroundPermission;
+                perm.descriptionRes = pendingPermissionInfo.descriptionRes;
+                perm.requestRes = pendingPermissionInfo.requestRes;
+                perm.setPackageName(tree.perm.getPackageName());
+                perm.setName(name);
                 uid = tree.uid;
             }
         }
     }
 
-    static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
-            @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
+    static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal,
+            @Nullable BasePermission bp, @NonNull ParsedPermission p,
+            @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees,
             boolean chatty) {
-        final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
+        final PackageSettingBase pkgSetting =
+                (PackageSettingBase) packageManagerInternal.getPackageSetting(pkg.getPackageName());
         // Allow system apps to redefine non-system permissions
-        if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) {
-            final boolean currentOwnerIsSystem = (bp.perm != null
-                    && bp.perm.owner.isSystem());
-            if (p.owner.isSystem()) {
+        if (bp != null && !Objects.equals(bp.sourcePackageName, p.getPackageName())) {
+            final boolean currentOwnerIsSystem;
+            if (bp.perm == null) {
+                currentOwnerIsSystem = false;
+            } else {
+                AndroidPackage currentPackage = packageManagerInternal.getPackage(
+                        bp.perm.getPackageName());
+                if (currentPackage == null) {
+                    currentOwnerIsSystem = false;
+                } else {
+                    currentOwnerIsSystem = currentPackage.isSystem();
+                }
+            }
+
+            if (pkg.isSystem()) {
                 if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
                     // It's a built-in permission and no owner, take ownership now
+                    p.flags |= PermissionInfo.FLAG_INSTALLED;
                     bp.sourcePackageSetting = pkgSetting;
                     bp.perm = p;
-                    bp.uid = pkg.applicationInfo.uid;
-                    bp.sourcePackageName = p.info.packageName;
-                    p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+                    bp.uid = pkg.getUid();
+                    bp.sourcePackageName = p.getPackageName();
                 } else if (!currentOwnerIsSystem) {
-                    String msg = "New decl " + p.owner + " of permission  "
-                            + p.info.name + " is system; overriding " + bp.sourcePackageName;
+                    String msg = "New decl " + pkg + " of permission  "
+                            + p.getName() + " is system; overriding " + bp.sourcePackageName;
                     PackageManagerService.reportSettingsProblem(Log.WARN, msg);
                     bp = null;
                 }
             }
         }
         if (bp == null) {
-            bp = new BasePermission(p.info.name, p.info.packageName, TYPE_NORMAL);
+            bp = new BasePermission(p.getName(), p.getPackageName(), TYPE_NORMAL);
         }
         StringBuilder r = null;
         if (bp.perm == null) {
             if (bp.sourcePackageName == null
-                    || bp.sourcePackageName.equals(p.info.packageName)) {
-                final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
+                    || bp.sourcePackageName.equals(p.getPackageName())) {
+                final BasePermission tree = findPermissionTree(permissionTrees, p.getName());
                 if (tree == null
-                        || tree.sourcePackageName.equals(p.info.packageName)) {
+                        || tree.sourcePackageName.equals(p.getPackageName())) {
+                    p.flags |= PermissionInfo.FLAG_INSTALLED;
                     bp.sourcePackageSetting = pkgSetting;
                     bp.perm = p;
-                    bp.uid = pkg.applicationInfo.uid;
-                    bp.sourcePackageName = p.info.packageName;
-                    p.info.flags |= PermissionInfo.FLAG_INSTALLED;
+                    bp.uid = pkg.getUid();
+                    bp.sourcePackageName = p.getPackageName();
                     if (chatty) {
                         if (r == null) {
                             r = new StringBuilder(256);
                         } else {
                             r.append(' ');
                         }
-                        r.append(p.info.name);
+                        r.append(p.getName());
                     }
                 } else {
-                    Slog.w(TAG, "Permission " + p.info.name + " from package "
-                            + p.info.packageName + " ignored: base tree "
+                    Slog.w(TAG, "Permission " + p.getName() + " from package "
+                            + p.getPackageName() + " ignored: base tree "
                             + tree.name + " is from package "
                             + tree.sourcePackageName);
                 }
             } else {
-                Slog.w(TAG, "Permission " + p.info.name + " from package "
-                        + p.info.packageName + " ignored: original from "
+                Slog.w(TAG, "Permission " + p.getName() + " from package "
+                        + p.getPackageName() + " ignored: original from "
                         + bp.sourcePackageName);
             }
         } else if (chatty) {
@@ -390,10 +398,10 @@
                 r.append(' ');
             }
             r.append("DUP:");
-            r.append(p.info.name);
+            r.append(p.getName());
         }
-        if (bp.perm == p) {
-            bp.protectionLevel = p.info.protectionLevel;
+        if (bp.perm != null && Objects.equals(bp.perm.className, p.className)) {
+            bp.protectionLevel = p.protectionLevel;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
             Log.d(TAG, "  Permissions: " + r);
@@ -417,17 +425,17 @@
         throw new SecurityException("No permission tree found for " + permName);
     }
 
-    public void enforceDeclaredUsedAndRuntimeOrDevelopment(PackageParser.Package pkg) {
-        final PackageSetting pkgSetting = (PackageSetting) pkg.mExtras;
+    public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
+            PackageSetting pkgSetting) {
         final PermissionsState permsState = pkgSetting.getPermissionsState();
-        int index = pkg.requestedPermissions.indexOf(name);
+        int index = pkg.getRequestedPermissions().indexOf(name);
         if (!permsState.hasRequestedPermission(name) && index == -1) {
-            throw new SecurityException("Package " + pkg.packageName
+            throw new SecurityException("Package " + pkg.getPackageName()
                     + " has not requested permission " + name);
         }
         if (!isRuntime() && !isDevelopment()) {
-            throw new SecurityException("Permission " + name
-                    + " requested by " + pkg.packageName + " is not a changeable permission type");
+            throw new SecurityException("Permission " + name + " requested by "
+                    + pkg.getPackageName() + " is not a changeable permission type");
         }
     }
 
@@ -445,12 +453,12 @@
 
     public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
         if (groupName == null) {
-            if (perm == null || perm.info.group == null) {
+            if (perm == null || perm.getGroup() == null) {
                 return generatePermissionInfo(protectionLevel, flags);
             }
         } else {
-            if (perm != null && groupName.equals(perm.info.group)) {
-                return PackageParser.generatePermissionInfo(perm, flags);
+            if (perm != null && groupName.equals(perm.getGroup())) {
+                return PackageInfoUtils.generatePermissionInfo(perm, flags);
             }
         }
         return null;
@@ -460,8 +468,8 @@
         PermissionInfo permissionInfo;
         if (perm != null) {
             final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
-            permissionInfo = PackageParser.generatePermissionInfo(perm, flags);
-            if (protectionLevelChanged && permissionInfo == perm.info) {
+            permissionInfo = PackageInfoUtils.generatePermissionInfo(perm, flags);
+            if (protectionLevelChanged) {
                 // if we return different protection level, don't use the cached info
                 permissionInfo = new PermissionInfo(permissionInfo);
                 permissionInfo.protectionLevel = adjustedProtectionLevel;
@@ -541,14 +549,18 @@
             serializer.attribute(null, "protection", Integer.toString(protectionLevel));
         }
         if (type == BasePermission.TYPE_DYNAMIC) {
-            final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
-            if (pi != null) {
+            if (perm != null || pendingPermissionInfo != null) {
                 serializer.attribute(null, "type", "dynamic");
-                if (pi.icon != 0) {
-                    serializer.attribute(null, "icon", Integer.toString(pi.icon));
+                int icon = perm != null ? perm.icon : pendingPermissionInfo.icon;
+                CharSequence nonLocalizedLabel = perm != null
+                        ? perm.nonLocalizedLabel
+                        : pendingPermissionInfo.nonLocalizedLabel;
+
+                if (icon != 0) {
+                    serializer.attribute(null, "icon", Integer.toString(icon));
                 }
-                if (pi.nonLocalizedLabel != null) {
-                    serializer.attribute(null, "label", pi.nonLocalizedLabel.toString());
+                if (nonLocalizedLabel != null) {
+                    serializer.attribute(null, "label", nonLocalizedLabel.toString());
                 }
             }
         }
@@ -568,14 +580,14 @@
         return s1.equals(s2);
     }
 
-    private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
+    private static boolean comparePermissionInfos(ParsedPermission pi1, PermissionInfo pi2) {
         if (pi1.icon != pi2.icon) return false;
         if (pi1.logo != pi2.logo) return false;
         if (pi1.protectionLevel != pi2.protectionLevel) return false;
-        if (!compareStrings(pi1.name, pi2.name)) return false;
+        if (!compareStrings(pi1.getName(), pi2.name)) return false;
         if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
         // We'll take care of setting this one.
-        if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+        if (!compareStrings(pi1.getPackageName(), pi2.packageName)) return false;
         // These are not currently stored in settings.
         //if (!compareStrings(pi1.group, pi2.group)) return false;
         //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -611,9 +623,9 @@
                 pw.println(PermissionInfo.protectionToString(protectionLevel));
         if (perm != null) {
             pw.print("    perm="); pw.println(perm);
-            if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0
-                    || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) {
-                pw.print("    flags=0x"); pw.println(Integer.toHexString(perm.info.flags));
+            if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0
+                    || (perm.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+                pw.print("    flags=0x"); pw.println(Integer.toHexString(perm.flags));
             }
         }
         if (sourcePackageSetting != null) {
@@ -625,4 +637,20 @@
         }
         return true;
     }
+
+    @Override
+    public String toString() {
+        return "BasePermission{" +
+                "name='" + name + '\'' +
+                ", type=" + type +
+                ", sourcePackageName='" + sourcePackageName + '\'' +
+                ", sourcePackageSetting=" + sourcePackageSetting +
+                ", protectionLevel=" + protectionLevel +
+                ", perm=" + perm +
+                ", pendingPermissionInfo=" + pendingPermissionInfo +
+                ", uid=" + uid +
+                ", gids=" + Arrays.toString(gids) +
+                ", perUser=" + perUser +
+                '}';
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b831374..fc8d520 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -64,10 +64,13 @@
 import android.content.pm.PackageManager.PermissionWhitelistFlags;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.content.pm.parsing.PackageInfoUtils;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.Binder;
@@ -420,7 +423,8 @@
         }
     }
 
-    @Nullable BasePermission getPermission(String permName) {
+    @Nullable
+    BasePermission getPermission(String permName) {
         synchronized (mLock) {
             return mSettings.getPermissionLocked(permName);
         }
@@ -454,10 +458,9 @@
         }
         synchronized (mLock) {
             final int n = mSettings.mPermissionGroups.size();
-            final ArrayList<PermissionGroupInfo> out =
-                    new ArrayList<PermissionGroupInfo>(n);
-            for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) {
-                out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
+            final ArrayList<PermissionGroupInfo> out = new ArrayList<>(n);
+            for (ParsedPermissionGroup pg : mSettings.mPermissionGroups.values()) {
+                out.add(PackageInfoUtils.generatePermissionGroupInfo(pg, flags));
             }
             return new ParceledListSlice<>(out);
         }
@@ -473,7 +476,7 @@
             return null;
         }
         synchronized (mLock) {
-            return PackageParser.generatePermissionGroupInfo(
+            return PackageInfoUtils.generatePermissionGroupInfo(
                     mSettings.mPermissionGroups.get(groupName), flags);
         }
     }
@@ -597,8 +600,13 @@
                 false, // requirePermissionWhenSameUser
                 "getPermissionFlags");
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg == null || pkg.mExtras == null) {
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+        if (pkg == null) {
+            return 0;
+        }
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
+        if (ps == null) {
             return 0;
         }
         synchronized (mLock) {
@@ -609,7 +617,6 @@
         if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
             return 0;
         }
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
         PermissionsState permissionsState = ps.getPermissionsState();
         return permissionsState.getPermissionFlags(permName, userId);
     }
@@ -696,8 +703,10 @@
             flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
         }
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg == null || pkg.mExtras == null) {
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                packageName);
+        if (pkg == null || ps == null) {
             Log.e(TAG, "Unknown package: " + packageName);
             return;
         }
@@ -713,7 +722,6 @@
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
 
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
         final PermissionsState permissionsState = ps.getPermissionsState();
         final boolean hadState =
                 permissionsState.getRuntimePermissionState(permName, userId) != null;
@@ -726,11 +734,11 @@
             // Install and runtime permissions are stored in different places,
             // so figure out what permission changed and persist the change.
             if (permissionsState.getInstallPermissionState(permName) != null) {
-                callback.onInstallPermissionUpdatedNotifyListener(pkg.applicationInfo.uid);
+                callback.onInstallPermissionUpdatedNotifyListener(pkg.getUid());
             } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
                     || hadState) {
-                callback.onPermissionUpdatedNotifyListener(new int[] { userId }, false,
-                        pkg.applicationInfo.uid);
+                callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false,
+                        pkg.getUid());
             }
         }
     }
@@ -762,18 +770,16 @@
                 ? flagValues : flagValues & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 
         final boolean[] changed = new boolean[1];
-        mPackageManagerInt.forEachPackage(new Consumer<PackageParser.Package>() {
-            @Override
-            public void accept(Package pkg) {
-                final PackageSetting ps = (PackageSetting) pkg.mExtras;
-                if (ps == null) {
-                    return;
-                }
-                final PermissionsState permissionsState = ps.getPermissionsState();
-                changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
-                        userId, effectiveFlagMask, effectiveFlagValues);
-                mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
+        mPackageManagerInt.forEachPackage(pkg -> {
+            final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                    pkg.getPackageName());
+            if (ps == null) {
+                return;
             }
+            final PermissionsState permissionsState = ps.getPermissionsState();
+            changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
+                    userId, effectiveFlagMask, effectiveFlagValues);
+            mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
         });
 
         if (changed[0]) {
@@ -804,7 +810,7 @@
 
     private int checkPermissionImpl(@NonNull String permissionName, @NonNull String packageName,
             @UserIdInt int userId) {
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
         if (pkg == null) {
             return PackageManager.PERMISSION_DENIED;
         }
@@ -812,11 +818,11 @@
                 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
 
-    private boolean checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
+    private boolean checkPermissionInternal(@NonNull AndroidPackage pkg, boolean isPackageExplicit,
             @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps,
             @UserIdInt int userId) {
         final int callingUid = getCallingUid();
-        if (isPackageExplicit || pkg.mSharedUserId == null) {
+        if (isPackageExplicit || pkg.getSharedUserId() == null) {
             if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
                 return false;
             }
@@ -826,8 +832,9 @@
             }
         }
 
-        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final int uid = UserHandle.getUid(userId, pkg.getUid());
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
         if (ps == null) {
             return false;
         }
@@ -858,9 +865,9 @@
             final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
             final int packageNamesSize = packageNames != null ? packageNames.length : 0;
             for (int i = 0; i < packageNamesSize; i++) {
-                final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageNames[i]);
-                if (pkg != null && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
-                        && pkg.requestedPermissions.contains(permissionName)) {
+                final AndroidPackage pkg = mPackageManagerInt.getPackage(packageNames[i]);
+                if (pkg != null && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
+                        && pkg.getRequestedPermissions().contains(permissionName)) {
                     hasPermission = true;
                     break;
                 }
@@ -901,7 +908,7 @@
     }
 
     private int checkUidPermissionImpl(@NonNull String permissionName, int uid) {
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
         return checkUidPermissionInternal(uid, pkg, permissionName, true)
                 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
@@ -913,7 +920,7 @@
      *
      * @see SystemConfig#getSystemPermissions()
      */
-    private boolean checkUidPermissionInternal(int uid, @Nullable Package pkg,
+    private boolean checkUidPermissionInternal(int uid, @Nullable AndroidPackage pkg,
             @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps) {
         if (pkg != null) {
             final int userId = UserHandle.getUserId(uid);
@@ -948,7 +955,7 @@
     }
 
     private boolean isUidPermissionGranted(int uid, @NonNull String permissionName) {
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
         return checkUidPermissionInternal(uid, pkg, permissionName, false);
     }
 
@@ -989,7 +996,7 @@
                     "getWhitelistedRestrictedPermissions for user " + userId);
         }
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
         if (pkg == null) {
             return null;
         }
@@ -1022,7 +1029,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             final PermissionsState permissionsState =
-                    PackageManagerServiceUtils.getPermissionsState(pkg);
+                    PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
             if (permissionsState == null) {
                 return null;
             }
@@ -1040,9 +1047,9 @@
 
             ArrayList<String> whitelistedPermissions = null;
 
-            final int permissionCount = pkg.requestedPermissions.size();
+            final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
             for (int i = 0; i < permissionCount; i++) {
-                final String permissionName = pkg.requestedPermissions.get(i);
+                final String permissionName = pkg.getRequestedPermissions().get(i);
                 final int currentFlags =
                         permissionsState.getPermissionFlags(permissionName, userId);
                 if ((currentFlags & queryFlags) != 0) {
@@ -1139,7 +1146,7 @@
                     "setWhitelistedRestrictedPermissions for user " + userId);
         }
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
         if (pkg == null) {
             return false;
         }
@@ -1168,7 +1175,7 @@
                         + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
             }
             final List<String> whitelistedPermissions =
-                    getWhitelistedRestrictedPermissions(pkg.packageName, flags, userId);
+                    getWhitelistedRestrictedPermissions(pkg.getPackageName(), flags, userId);
             if (permissions == null || permissions.isEmpty()) {
                 if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) {
                     return true;
@@ -1242,8 +1249,10 @@
                 false, // requirePermissionWhenSameUser
                 "grantRuntimePermission");
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg == null || pkg.mExtras == null) {
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                packageName);
+        if (pkg == null || ps == null) {
             Log.e(TAG, "Unknown package: " + packageName);
             return;
         }
@@ -1258,21 +1267,19 @@
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps);
 
         // If a permission review is required for legacy apps we represent
         // their permissions as always granted runtime ones since we need
         // to keep the review required permission flag per user while an
         // install permission's state is shared across all users.
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
                 && bp.isRuntime()) {
             return;
         }
 
-        final int uid = UserHandle.getUid(userId,
-                UserHandle.getAppId(pkg.applicationInfo.uid));
+        final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
 
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
         final PermissionsState permissionsState = ps.getPermissionsState();
 
         final int flags = permissionsState.getPermissionFlags(permName, userId);
@@ -1295,7 +1302,7 @@
         }
 
         if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
-                pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {
+                pkg.toAppInfo(), UserHandle.of(userId), permName).mayGrantPermission()) {
             Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
                     + packageName);
             return;
@@ -1318,7 +1325,7 @@
                     + permName + " for package " + packageName);
         }
 
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
             Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
             return;
         }
@@ -1331,7 +1338,7 @@
 
             case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                 if (callback != null) {
-                    callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
+                    callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
                 }
             }
             break;
@@ -1404,8 +1411,10 @@
                 false, // requirePermissionWhenSameUser
                 "revokeRuntimePermission");
 
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
-        if (pkg == null || pkg.mExtras == null) {
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                packageName);
+        if (pkg == null || ps == null) {
             Log.e(TAG, "Unknown package: " + packageName);
             return;
         }
@@ -1417,18 +1426,17 @@
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
+        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps);
 
         // If a permission review is required for legacy apps we represent
         // their permissions as always granted runtime ones since we need
         // to keep the review required permission flag per user while an
         // install permission's state is shared across all users.
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
                 && bp.isRuntime()) {
             return;
         }
 
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
         final PermissionsState permissionsState = ps.getPermissionsState();
 
         final int flags = permissionsState.getPermissionFlags(permName, userId);
@@ -1471,7 +1479,7 @@
 
         if (callback != null) {
             callback.onPermissionRevoked(UserHandle.getUid(userId,
-                    UserHandle.getAppId(pkg.applicationInfo.uid)), userId);
+                    UserHandle.getAppId(pkg.getUid())), userId);
         }
 
         if (bp.isRuntime()) {
@@ -1496,7 +1504,7 @@
                 StorageManager.UUID_PRIVATE_INTERNAL, false, mDefaultPermissionCallback);
         for (final int userId : UserManagerService.getInstance().getUserIds()) {
             mPackageManagerInt.forEachPackage(
-                    (PackageParser.Package pkg) -> resetRuntimePermissionsInternal(pkg, userId));
+                    (AndroidPackage pkg) -> resetRuntimePermissionsInternal(pkg, userId));
         }
     }
 
@@ -1507,9 +1515,9 @@
      * @param userId The device user for which to do a reset.
      */
     @GuardedBy("mLock")
-    private void resetRuntimePermissionsInternal(final PackageParser.Package pkg,
+    private void resetRuntimePermissionsInternal(final AndroidPackage pkg,
             final int userId) {
-        final String packageName = pkg.packageName;
+        final String packageName = pkg.getPackageName();
 
         // These are flags that can change base on user actions.
         final int userSettableMask = FLAG_PERMISSION_USER_SET
@@ -1521,7 +1529,7 @@
                 | FLAG_PERMISSION_POLICY_FIXED;
 
         // Delay and combine non-async permission callbacks
-        final int permissionCount = pkg.requestedPermissions.size();
+        final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
         final boolean[] permissionRemoved = new boolean[1];
         final ArraySet<Long> revokedPermissions = new ArraySet<>();
         final IntArray syncUpdatedUsers = new IntArray(permissionCount);
@@ -1579,7 +1587,7 @@
 
         final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
         for (int i = 0; i < permissionCount; i++) {
-            final String permName = pkg.requestedPermissions.get(i);
+            final String permName = pkg.getRequestedPermissions().get(i);
             final BasePermission bp;
             synchronized (mLock) {
                 bp = mSettings.getPermissionLocked(permName);
@@ -1594,7 +1602,7 @@
 
             // If shared user we just reset the state to which only this app contributed.
             final String sharedUserId =
-                    mPackageManagerInt.getSharedUserIdForPackage(pkg.packageName);
+                    mPackageManagerInt.getSharedUserIdForPackage(pkg.getPackageName());
             final String[] pkgNames =
                     mPackageManagerInt.getPackagesForSharedUserId(sharedUserId, userId);
             if (pkgNames != null && pkgNames.length > 0) {
@@ -1602,10 +1610,10 @@
                 final int packageCount = pkgNames.length;
                 for (int j = 0; j < packageCount; j++) {
                     final String sharedPkgName = pkgNames[j];
-                    final PackageParser.Package sharedPkg =
+                    final AndroidPackage sharedPkg =
                             mPackageManagerInt.getPackage(sharedPkgName);
-                    if (sharedPkg != null && !sharedPkg.packageName.equals(packageName)
-                            && sharedPkg.requestedPermissions.contains(permName)) {
+                    if (sharedPkg != null && !sharedPkg.getPackageName().equals(packageName)
+                            && sharedPkg.getRequestedPermissions().contains(permName)) {
                         used = true;
                         break;
                     }
@@ -1697,10 +1705,11 @@
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             return null;
         }
+        DefaultBrowserProvider provider;
         synchronized (mLock) {
-            return mDefaultBrowserProvider == null
-                    ? null : mDefaultBrowserProvider.getDefaultBrowser(userId);
+            provider = mDefaultBrowserProvider;
         }
+        return provider != null ? provider.getDefaultBrowser(userId) : null;
     }
 
     @Override
@@ -1716,23 +1725,27 @@
 
     private boolean setDefaultBrowserInternal(String packageName, boolean async,
             boolean doGrant, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return false;
+        }
+        DefaultBrowserProvider provider;
         synchronized (mLock) {
-            if (userId == UserHandle.USER_ALL) {
+            provider = mDefaultBrowserProvider;
+        }
+        if (provider == null) {
+            return false;
+        }
+        if (async) {
+            provider.setDefaultBrowserAsync(packageName, userId);
+        } else {
+            if (!provider.setDefaultBrowser(packageName, userId)) {
                 return false;
             }
-            if (mDefaultBrowserProvider == null) {
-                return false;
-            }
-            if (async) {
-                mDefaultBrowserProvider.setDefaultBrowserAsync(packageName, userId);
-            } else {
-                if (!mDefaultBrowserProvider.setDefaultBrowser(packageName, userId)) {
-                    return false;
-                }
-            }
-            if (doGrant && packageName != null) {
-                mDefaultPermissionGrantPolicy
-                        .grantDefaultPermissionsToDefaultBrowser(packageName, userId);
+        }
+        if (doGrant && packageName != null) {
+            synchronized (mLock) {
+                mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
+                        userId);
             }
         }
         return true;
@@ -2028,15 +2041,16 @@
             return protectionLevel;
         }
         // Normalize package name to handle renamed packages and static libs
-        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+        final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
         if (pkg == null) {
             return protectionLevel;
         }
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
             return protectionLevelMasked;
         }
         // Apps that target O see flags for all protection levels.
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
         if (ps == null) {
             return protectionLevel;
         }
@@ -2057,35 +2071,35 @@
      * @param permissionCallback Callback for permission changed
      */
     private void revokeRuntimePermissionsIfGroupChanged(
-            @NonNull PackageParser.Package newPackage,
-            @NonNull PackageParser.Package oldPackage,
+            @NonNull AndroidPackage newPackage,
+            @NonNull AndroidPackage oldPackage,
             @NonNull ArrayList<String> allPackageNames,
             @NonNull PermissionCallback permissionCallback) {
-        final int numOldPackagePermissions = oldPackage.permissions.size();
+        final int numOldPackagePermissions = ArrayUtils.size(oldPackage.getPermissions());
         final ArrayMap<String, String> oldPermissionNameToGroupName
                 = new ArrayMap<>(numOldPackagePermissions);
 
         for (int i = 0; i < numOldPackagePermissions; i++) {
-            final PackageParser.Permission permission = oldPackage.permissions.get(i);
+            final ParsedPermission permission = oldPackage.getPermissions().get(i);
 
-            if (permission.group != null) {
-                oldPermissionNameToGroupName.put(permission.info.name,
-                        permission.group.info.name);
+            if (permission.parsedPermissionGroup != null) {
+                oldPermissionNameToGroupName.put(permission.getName(),
+                        permission.parsedPermissionGroup.getName());
             }
         }
 
         final int callingUid = Binder.getCallingUid();
-        final int numNewPackagePermissions = newPackage.permissions.size();
+        final int numNewPackagePermissions = ArrayUtils.size(newPackage.getPermissions());
         for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions;
                 newPermissionNum++) {
-            final PackageParser.Permission newPermission =
-                    newPackage.permissions.get(newPermissionNum);
-            final int newProtection = newPermission.info.getProtection();
+            final ParsedPermission newPermission =
+                    newPackage.getPermissions().get(newPermissionNum);
+            final int newProtection = newPermission.getProtection();
 
             if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
-                final String permissionName = newPermission.info.name;
-                final String newPermissionGroupName =
-                        newPermission.group == null ? null : newPermission.group.info.name;
+                final String permissionName = newPermission.getName();
+                final String newPermissionGroupName = newPermission.parsedPermissionGroup == null
+                        ? null : newPermission.parsedPermissionGroup.getName();
                 final String oldPermissionGroupName = oldPermissionNameToGroupName.get(
                         permissionName);
 
@@ -2103,7 +2117,7 @@
                                     userId);
                             if (permissionState == PackageManager.PERMISSION_GRANTED) {
                                 EventLog.writeEvent(0x534e4554, "72710897",
-                                        newPackage.applicationInfo.uid,
+                                        newPackage.getUid(),
                                         "Revoking permission " + permissionName +
                                         " from package " + packageName +
                                         " as the group changed from " + oldPermissionGroupName +
@@ -2124,54 +2138,56 @@
         }
     }
 
-    private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
-        final int N = pkg.permissions.size();
+    private void addAllPermissions(AndroidPackage pkg, boolean chatty) {
+        final int N = ArrayUtils.size(pkg.getPermissions());
         for (int i=0; i<N; i++) {
-            PackageParser.Permission p = pkg.permissions.get(i);
+            ParsedPermission p = pkg.getPermissions().get(i);
 
             // Assume by default that we did not install this permission into the system.
-            p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
+            p.flags &= ~PermissionInfo.FLAG_INSTALLED;
 
             synchronized (PermissionManagerService.this.mLock) {
                 // Now that permission groups have a special meaning, we ignore permission
                 // groups for legacy apps to prevent unexpected behavior. In particular,
                 // permissions for one app being granted to someone just because they happen
                 // to be in a group defined by another app (before this had no implications).
-                if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
-                    p.group = mSettings.mPermissionGroups.get(p.info.group);
+                if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
+                    p.parsedPermissionGroup = mSettings.mPermissionGroups.get(p.getGroup());
                     // Warn for a permission in an unknown group.
                     if (DEBUG_PERMISSIONS
-                            && p.info.group != null && p.group == null) {
-                        Slog.i(TAG, "Permission " + p.info.name + " from package "
-                                + p.info.packageName + " in an unknown group " + p.info.group);
+                            && p.getGroup() != null && p.parsedPermissionGroup == null) {
+                        Slog.i(TAG, "Permission " + p.getName() + " from package "
+                                + p.getPackageName() + " in an unknown group " + p.getGroup());
                     }
                 }
 
                 if (p.tree) {
                     final BasePermission bp = BasePermission.createOrUpdate(
-                            mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
+                            mPackageManagerInt,
+                            mSettings.getPermissionTreeLocked(p.getName()), p, pkg,
                             mSettings.getAllPermissionTreesLocked(), chatty);
-                    mSettings.putPermissionTreeLocked(p.info.name, bp);
+                    mSettings.putPermissionTreeLocked(p.getName(), bp);
                 } else {
                     final BasePermission bp = BasePermission.createOrUpdate(
-                            mSettings.getPermissionLocked(p.info.name),
+                            mPackageManagerInt,
+                            mSettings.getPermissionLocked(p.getName()),
                             p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
-                    mSettings.putPermissionLocked(p.info.name, bp);
+                    mSettings.putPermissionLocked(p.getName(), bp);
                 }
             }
         }
     }
 
-    private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) {
-        final int N = pkg.permissionGroups.size();
+    private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
+        final int N = ArrayUtils.size(pkg.getPermissionGroups());
         StringBuilder r = null;
         for (int i=0; i<N; i++) {
-            final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
-            final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name);
-            final String curPackageName = (cur == null) ? null : cur.info.packageName;
-            final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
+            final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i);
+            final ParsedPermissionGroup cur = mSettings.mPermissionGroups.get(pg.getName());
+            final String curPackageName = (cur == null) ? null : cur.getPackageName();
+            final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName);
             if (cur == null || isPackageUpdate) {
-                mSettings.mPermissionGroups.put(pg.info.name, pg);
+                mSettings.mPermissionGroups.put(pg.getName(), pg);
                 if (chatty && DEBUG_PACKAGE_SCANNING) {
                     if (r == null) {
                         r = new StringBuilder(256);
@@ -2181,12 +2197,12 @@
                     if (isPackageUpdate) {
                         r.append("UPD:");
                     }
-                    r.append(pg.info.name);
+                    r.append(pg.getName());
                 }
             } else {
-                Slog.w(TAG, "Permission group " + pg.info.name + " from package "
-                        + pg.info.packageName + " ignored: original from "
-                        + cur.info.packageName);
+                Slog.w(TAG, "Permission group " + pg.getName() + " from package "
+                        + pg.getPackageName() + " ignored: original from "
+                        + cur.getPackageName());
                 if (chatty && DEBUG_PACKAGE_SCANNING) {
                     if (r == null) {
                         r = new StringBuilder(256);
@@ -2194,7 +2210,7 @@
                         r.append(' ');
                     }
                     r.append("DUP:");
-                    r.append(pg.info.name);
+                    r.append(pg.getName());
                 }
             }
         }
@@ -2204,15 +2220,15 @@
 
     }
 
-    private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+    private void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
         synchronized (mLock) {
-            int N = pkg.permissions.size();
+            int N = ArrayUtils.size(pkg.getPermissions());
             StringBuilder r = null;
             for (int i=0; i<N; i++) {
-                PackageParser.Permission p = pkg.permissions.get(i);
-                BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
+                ParsedPermission p = pkg.getPermissions().get(i);
+                BasePermission bp = mSettings.mPermissions.get(p.getName());
                 if (bp == null) {
-                    bp = mSettings.mPermissionTrees.get(p.info.name);
+                    bp = mSettings.mPermissionTrees.get(p.getName());
                 }
                 if (bp != null && bp.isPermission(p)) {
                     bp.setPermission(null);
@@ -2222,14 +2238,14 @@
                         } else {
                             r.append(' ');
                         }
-                        r.append(p.info.name);
+                        r.append(p.getName());
                     }
                 }
                 if (p.isAppOp()) {
                     ArraySet<String> appOpPkgs =
-                            mSettings.mAppOpPermissionPackages.get(p.info.name);
+                            mSettings.mAppOpPermissionPackages.get(p.getName());
                     if (appOpPkgs != null) {
-                        appOpPkgs.remove(pkg.packageName);
+                        appOpPkgs.remove(pkg.getPackageName());
                     }
                 }
             }
@@ -2237,14 +2253,14 @@
                 if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
             }
 
-            N = pkg.requestedPermissions.size();
+            N = pkg.getRequestedPermissions().size();
             r = null;
             for (int i=0; i<N; i++) {
-                String perm = pkg.requestedPermissions.get(i);
+                String perm = pkg.getRequestedPermissions().get(i);
                 if (mSettings.isPermissionAppOp(perm)) {
                     ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
                     if (appOpPkgs != null) {
-                        appOpPkgs.remove(pkg.packageName);
+                        appOpPkgs.remove(pkg.getPackageName());
                         if (appOpPkgs.isEmpty()) {
                             mSettings.mAppOpPermissionPackages.remove(perm);
                         }
@@ -2273,7 +2289,7 @@
      * @param packageOfInterest If this is the name of {@code pkg} add extra logging
      * @param callback Result call back
      */
-    private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,
+    private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace,
             @Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
         // IMPORTANT: There are two types of permissions: install and runtime.
         // Install time permissions are granted when the app is installed to
@@ -2286,7 +2302,8 @@
         // being upgraded to target a newer SDK, in which case dangerous permissions
         // are transformed from install time to runtime ones.
 
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
         if (ps == null) {
             return;
         }
@@ -2327,23 +2344,25 @@
         synchronized (mLock) {
             ArraySet<String> newImplicitPermissions = new ArraySet<>();
 
-            final int N = pkg.requestedPermissions.size();
+            final int N = pkg.getRequestedPermissions().size();
             for (int i = 0; i < N; i++) {
-                final String permName = pkg.requestedPermissions.get(i);
+                final String permName = pkg.getRequestedPermissions().get(i);
                 final BasePermission bp = mSettings.getPermissionLocked(permName);
                 final boolean appSupportsRuntimePermissions =
-                        pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
+                        pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
                 String upgradedActivityRecognitionPermission = null;
 
                 if (DEBUG_INSTALL) {
-                    Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
+                    Log.i(TAG, "Package " + pkg.getPackageName()
+                            + " checking " + permName + ": " + bp);
                 }
 
                 if (bp == null || bp.getSourcePackageSetting() == null) {
-                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
+                    if (packageOfInterest == null || packageOfInterest.equals(
+                            pkg.getPackageName())) {
                         if (DEBUG_PERMISSIONS) {
                             Slog.i(TAG, "Unknown permission " + permName
-                                    + " in package " + pkg.packageName);
+                                    + " in package " + pkg.getPackageName());
                         }
                     }
                     continue;
@@ -2352,14 +2371,14 @@
                 // Cache newImplicitPermissions before modifing permissionsState as for the shared
                 // uids the original and new state are the same object
                 if (!origPermissions.hasRequestedPermission(permName)
-                        && (pkg.implicitPermissions.contains(permName)
+                        && (pkg.getImplicitPermissions().contains(permName)
                                 || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
-                    if (pkg.implicitPermissions.contains(permName)) {
+                    if (pkg.getImplicitPermissions().contains(permName)) {
                         // If permName is an implicit permission, try to auto-grant
                         newImplicitPermissions.add(permName);
 
                         if (DEBUG_PERMISSIONS) {
-                            Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
+                            Slog.i(TAG, permName + " is newly added for " + pkg.getPackageName());
                         }
                     } else {
                         // Special case for Activity Recognition permission. Even if AR permission
@@ -2382,7 +2401,7 @@
 
                                 if (DEBUG_PERMISSIONS) {
                                     Slog.i(TAG, permName + " is newly added for "
-                                            + pkg.packageName);
+                                            + pkg.getPackageName());
                                 }
                                 break;
                             }
@@ -2391,10 +2410,10 @@
                 }
 
                 // Limit ephemeral apps to ephemeral allowed permissions.
-                if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
+                if (pkg.isInstantApp() && !bp.isInstant()) {
                     if (DEBUG_PERMISSIONS) {
                         Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
-                                + " for package " + pkg.packageName);
+                                + " for package " + pkg.getPackageName());
                     }
                     continue;
                 }
@@ -2402,7 +2421,7 @@
                 if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
                     if (DEBUG_PERMISSIONS) {
                         Log.i(TAG, "Denying runtime-only permission " + bp.getName()
-                                + " for package " + pkg.packageName);
+                                + " for package " + pkg.getPackageName());
                     }
                     continue;
                 }
@@ -2413,7 +2432,7 @@
 
                 // Keep track of app op permissions.
                 if (bp.isAppOp()) {
-                    mSettings.addAppOpPackage(perm, pkg.packageName);
+                    mSettings.addAppOpPackage(perm, pkg.getPackageName());
                 }
 
                 if (bp.isNormal()) {
@@ -2439,7 +2458,7 @@
 
                 if (DEBUG_PERMISSIONS) {
                     Slog.i(TAG, "Considering granting permission " + perm + " to package "
-                            + pkg.packageName);
+                            + pkg.getPackageName());
                 }
 
                 if (grant != GRANT_DENIED) {
@@ -2725,10 +2744,10 @@
 
                         default: {
                             if (packageOfInterest == null
-                                    || packageOfInterest.equals(pkg.packageName)) {
+                                    || packageOfInterest.equals(pkg.getPackageName())) {
                                 if (DEBUG_PERMISSIONS) {
                                     Slog.i(TAG, "Not granting permission " + perm
-                                            + " to package " + pkg.packageName
+                                            + " to package " + pkg.getPackageName()
                                             + " because it was previously installed without");
                                 }
                             }
@@ -2743,9 +2762,9 @@
                         changedInstallPermission = true;
                         if (DEBUG_PERMISSIONS) {
                             Slog.i(TAG, "Un-granting permission " + perm
-                                    + " from package " + pkg.packageName
+                                    + " from package " + pkg.getPackageName()
                                     + " (protectionLevel=" + bp.getProtectionLevel()
-                                    + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+                                    + " flags=0x" + Integer.toHexString(pkg.getFlags())
                                     + ")");
                         }
                     } else if (bp.isAppOp()) {
@@ -2753,11 +2772,11 @@
                         // not to be granted, there is a UI for the user to decide.
                         if (DEBUG_PERMISSIONS
                                 && (packageOfInterest == null
-                                        || packageOfInterest.equals(pkg.packageName))) {
+                                        || packageOfInterest.equals(pkg.getPackageName()))) {
                             Slog.i(TAG, "Not granting permission " + perm
-                                    + " to package " + pkg.packageName
+                                    + " to package " + pkg.getPackageName()
                                     + " (protectionLevel=" + bp.getProtectionLevel()
-                                    + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+                                    + " flags=0x" + Integer.toHexString(pkg.getFlags())
                                     + ")");
                         }
                     }
@@ -2787,7 +2806,7 @@
         }
 
         for (int userId : updatedUserIds) {
-            notifyRuntimePermissionStateChanged(pkg.packageName, userId);
+            notifyRuntimePermissionStateChanged(pkg.getPackageName(), userId);
         }
     }
 
@@ -2803,10 +2822,10 @@
      * @return The updated value of the {@code updatedUserIds} parameter
      */
     private @NonNull int[] revokePermissionsNoLongerImplicitLocked(
-            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
             @NonNull int[] updatedUserIds) {
-        String pkgName = pkg.packageName;
-        boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+        String pkgName = pkg.getPackageName();
+        boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
                 >= Build.VERSION_CODES.M;
 
         int[] users = UserManagerService.getInstance().getUserIds();
@@ -2815,7 +2834,7 @@
             int userId = users[i];
 
             for (String permission : ps.getPermissions(userId)) {
-                if (!pkg.implicitPermissions.contains(permission)) {
+                if (!pkg.getImplicitPermissions().contains(permission)) {
                     if (!ps.hasInstallPermission(permission)) {
                         int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
 
@@ -2865,9 +2884,9 @@
      */
     private void inheritPermissionStateToNewImplicitPermissionLocked(
             @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
-            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
-        String pkgName = pkg.packageName;
+        String pkgName = pkg.getPackageName();
         boolean isGranted = false;
         int flags = 0;
 
@@ -2914,10 +2933,10 @@
      * @return The ids of the users that are changed
      */
     private @NonNull int[] checkIfLegacyStorageOpsNeedToBeUpdated(
-            @NonNull PackageParser.Package pkg, boolean replace, @NonNull int[] updatedUserIds) {
-        if (replace && pkg.applicationInfo.hasRequestedLegacyExternalStorage() && (
-                pkg.requestedPermissions.contains(READ_EXTERNAL_STORAGE)
-                        || pkg.requestedPermissions.contains(WRITE_EXTERNAL_STORAGE))) {
+            @NonNull AndroidPackage pkg, boolean replace, @NonNull int[] updatedUserIds) {
+        if (replace && pkg.hasRequestedLegacyExternalStorage() && (
+                pkg.getRequestedPermissions().contains(READ_EXTERNAL_STORAGE)
+                        || pkg.getRequestedPermissions().contains(WRITE_EXTERNAL_STORAGE))) {
             return UserManagerService.getInstance().getUserIds();
         }
 
@@ -2936,10 +2955,10 @@
      */
     private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
             @NonNull PermissionsState origPs,
-            @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+            @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
             @NonNull ArraySet<String> newImplicitPermissions,
             @NonNull int[] updatedUserIds) {
-        String pkgName = pkg.packageName;
+        String pkgName = pkg.getPackageName();
         ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
 
         final List<SplitPermissionInfoParcelable> permissionList = getSplitPermissions();
@@ -3019,17 +3038,17 @@
                 SystemConfig.getInstance().getSplitPermissions());
     }
 
-    private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
+    private boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
         boolean allowed = false;
         final int NP = PackageParser.NEW_PERMISSIONS.length;
         for (int ip=0; ip<NP; ip++) {
             final PackageParser.NewPermissionInfo npi
                     = PackageParser.NEW_PERMISSIONS[ip];
             if (npi.name.equals(perm)
-                    && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
+                    && pkg.getTargetSdkVersion() < npi.sdkVersion) {
                 allowed = true;
                 Log.i(TAG, "Auto-granting " + perm + " to old pkg "
-                        + pkg.packageName);
+                        + pkg.getPackageName());
                 break;
             }
         }
@@ -3043,29 +3062,26 @@
      *
      * <p>This handles parent/child apps.
      */
-    private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
-        ArraySet<String> wlPermissions = null;
+    private boolean hasPrivappWhitelistEntry(String perm, AndroidPackage pkg) {
+        ArraySet<String> wlPermissions;
         if (pkg.isVendor()) {
             wlPermissions =
-                    SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName);
+                    SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.getPackageName());
         } else if (pkg.isProduct()) {
             wlPermissions =
-                    SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName);
+                    SystemConfig.getInstance().getProductPrivAppPermissions(pkg.getPackageName());
         } else if (pkg.isSystemExt()) {
             wlPermissions =
                     SystemConfig.getInstance().getSystemExtPrivAppPermissions(
-                            pkg.packageName);
+                            pkg.getPackageName());
         } else {
-            wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName);
+            wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.getPackageName());
         }
-        // Let's check if this package is whitelisted...
-        boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
-        // If it's not, we'll also tail-recurse to the parent.
-        return whitelisted ||
-                pkg.parentPackage != null && hasPrivappWhitelistEntry(perm, pkg.parentPackage);
+
+        return wlPermissions != null && wlPermissions.contains(perm);
     }
 
-    private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
+    private boolean grantSignaturePermission(String perm, AndroidPackage pkg,
             BasePermission bp, PermissionsState origPermissions) {
         boolean oemPermission = bp.isOEM();
         boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
@@ -3073,7 +3089,7 @@
         boolean privappPermissionsDisable =
                 RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
         boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
-        boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
+        boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName());
         if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
                 && !platformPackage && platformPermission) {
             if (!hasPrivappWhitelistEntry(perm, pkg)) {
@@ -3083,22 +3099,22 @@
                     ArraySet<String> deniedPermissions = null;
                     if (pkg.isVendor()) {
                         deniedPermissions = SystemConfig.getInstance()
-                                .getVendorPrivAppDenyPermissions(pkg.packageName);
+                                .getVendorPrivAppDenyPermissions(pkg.getPackageName());
                     } else if (pkg.isProduct()) {
                         deniedPermissions = SystemConfig.getInstance()
-                                .getProductPrivAppDenyPermissions(pkg.packageName);
+                                .getProductPrivAppDenyPermissions(pkg.getPackageName());
                     } else if (pkg.isSystemExt()) {
                         deniedPermissions = SystemConfig.getInstance()
-                                .getSystemExtPrivAppDenyPermissions(pkg.packageName);
+                                .getSystemExtPrivAppDenyPermissions(pkg.getPackageName());
                     } else {
                         deniedPermissions = SystemConfig.getInstance()
-                                .getPrivAppDenyPermissions(pkg.packageName);
+                                .getPrivAppDenyPermissions(pkg.getPackageName());
                     }
                     final boolean permissionViolation =
                             deniedPermissions == null || !deniedPermissions.contains(perm);
                     if (permissionViolation) {
                         Slog.w(TAG, "Privileged permission " + perm + " for package "
-                                + pkg.packageName + " (" + pkg.codePath
+                                + pkg.getPackageName() + " (" + pkg.getCodePath()
                                 + ") not in privapp-permissions whitelist");
 
                         if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
@@ -3106,7 +3122,7 @@
                                 mPrivappPermissionsViolations = new ArraySet<>();
                             }
                             mPrivappPermissionsViolations.add(
-                                    pkg.packageName + " (" + pkg.codePath + "): " + perm);
+                                    pkg.getPackageName() + " (" + pkg.getCodePath() + "): " + perm);
                         }
                     } else {
                         return false;
@@ -3119,7 +3135,7 @@
         }
         final String systemPackageName = mPackageManagerInt.getKnownPackageName(
                 PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
-        final PackageParser.Package systemPackage =
+        final AndroidPackage systemPackage =
                 mPackageManagerInt.getPackage(systemPackageName);
 
         // check if the package is allow to use this signature permission.  A package is allowed to
@@ -3130,24 +3146,23 @@
         //       package, and the defining package still trusts the old certificate for permissions
         //     - or it shares the above relationships with the system package
         boolean allowed =
-                pkg.mSigningDetails.hasAncestorOrSelf(
+                pkg.getSigningDetails().hasAncestorOrSelf(
                         bp.getSourcePackageSetting().getSigningDetails())
                 || bp.getSourcePackageSetting().getSigningDetails().checkCapability(
-                        pkg.mSigningDetails,
+                        pkg.getSigningDetails(),
                         PackageParser.SigningDetails.CertCapabilities.PERMISSION)
-                || pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
-                || systemPackage.mSigningDetails.checkCapability(
-                        pkg.mSigningDetails,
+                || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
+                || systemPackage.getSigningDetails().checkCapability(
+                        pkg.getSigningDetails(),
                         PackageParser.SigningDetails.CertCapabilities.PERMISSION);
         if (!allowed && (privilegedPermission || oemPermission)) {
             if (pkg.isSystem()) {
                 // For updated system applications, a privileged/oem permission
                 // is granted only if it had been defined by the original application.
                 if (pkg.isUpdatedSystemApp()) {
-                    final PackageParser.Package disabledPkg =
-                            mPackageManagerInt.getDisabledSystemPackage(pkg.packageName);
-                    final PackageSetting disabledPs =
-                            (disabledPkg != null) ? (PackageSetting) disabledPkg.mExtras : null;
+                    final PackageSetting disabledPs = (PackageSetting) mPackageManagerInt
+                            .getDisabledSystemPackage(pkg.getPackageName());
+                    final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
                     if (disabledPs != null
                             && disabledPs.getPermissionsState().hasInstallPermission(perm)) {
                         // If the original was granted this permission, we take
@@ -3172,40 +3187,10 @@
                                                 && canGrantOemPermission(disabledPs, perm)))) {
                             allowed = true;
                         }
-                        // Also if a privileged parent package on the system image or any of
-                        // its children requested a privileged/oem permission, the updated child
-                        // packages can also get the permission.
-                        if (pkg.parentPackage != null) {
-                            final PackageParser.Package disabledParentPkg = mPackageManagerInt
-                                    .getDisabledSystemPackage(pkg.parentPackage.packageName);
-                            final PackageSetting disabledParentPs = (disabledParentPkg != null)
-                                    ? (PackageSetting) disabledParentPkg.mExtras : null;
-                            if (disabledParentPkg != null
-                                    && ((privilegedPermission && disabledParentPs.isPrivileged())
-                                            || (oemPermission && disabledParentPs.isOem()))) {
-                                if (isPackageRequestingPermission(disabledParentPkg, perm)
-                                        && canGrantOemPermission(disabledParentPs, perm)) {
-                                    allowed = true;
-                                } else if (disabledParentPkg.childPackages != null) {
-                                    for (PackageParser.Package disabledChildPkg
-                                            : disabledParentPkg.childPackages) {
-                                        final PackageSetting disabledChildPs =
-                                                (disabledChildPkg != null)
-                                                        ? (PackageSetting) disabledChildPkg.mExtras
-                                                        : null;
-                                        if (isPackageRequestingPermission(disabledChildPkg, perm)
-                                                && canGrantOemPermission(
-                                                        disabledChildPs, perm)) {
-                                            allowed = true;
-                                            break;
-                                        }
-                                    }
-                                }
-                            }
-                        }
                     }
                 } else {
-                    final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                    final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                            pkg.getPackageName());
                     allowed = (privilegedPermission && pkg.isPrivileged())
                             || (oemPermission && pkg.isOem()
                                     && canGrantOemPermission(ps, perm));
@@ -3216,7 +3201,8 @@
                 if (allowed && privilegedPermission &&
                         !vendorPrivilegedPermission && pkg.isVendor()) {
                    Slog.w(TAG, "Permission " + perm + " cannot be granted to privileged vendor apk "
-                           + pkg.packageName + " because it isn't a 'vendorPrivileged' permission.");
+                           + pkg.getPackageName()
+                           + " because it isn't a 'vendorPrivileged' permission.");
                    allowed = false;
                 }
             }
@@ -3224,7 +3210,7 @@
         if (!allowed) {
             if (!allowed
                     && bp.isPre23()
-                    && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                    && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
                 // If this was a previously normal/dangerous permission that got moved
                 // to a system permission as part of the runtime permission redesign, then
                 // we still want to blindly grant it to old apps.
@@ -3234,9 +3220,9 @@
             //                  need a separate flag anymore. Hence we need to check which
             //                  permissions are needed by the permission controller
             if (!allowed && bp.isInstaller()
-                    && (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && (pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
-                    || pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    || pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
                             UserHandle.USER_SYSTEM)))) {
                 // If this permission is to be granted to the system installer and
@@ -3244,7 +3230,7 @@
                 allowed = true;
             }
             if (!allowed && bp.isVerifier()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
                 // If this permission is to be granted to the system verifier and
                 // this app is a verifier, then it gets the permission.
@@ -3261,41 +3247,41 @@
                 allowed = origPermissions.hasInstallPermission(perm);
             }
             if (!allowed && bp.isSetup()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
                 // If this permission is to be granted to the system setup wizard and
                 // this app is a setup wizard, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isSystemTextClassifier()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
                             UserHandle.USER_SYSTEM))) {
                 // Special permissions for the system default text classifier.
                 allowed = true;
             }
             if (!allowed && bp.isConfigurator()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                     PackageManagerInternal.PACKAGE_CONFIGURATOR,
                     UserHandle.USER_SYSTEM))) {
                 // Special permissions for the device configurator.
                 allowed = true;
             }
             if (!allowed && bp.isWellbeing()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                     PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
                 // Special permission granted only to the OEM specified wellbeing app
                 allowed = true;
             }
             if (!allowed && bp.isDocumenter()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
                 // If this permission is to be granted to the documenter and
                 // this app is the documenter, then it gets the permission.
                 allowed = true;
             }
             if (!allowed && bp.isIncidentReportApprover()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                             PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
                             UserHandle.USER_SYSTEM))) {
                 // If this permission is to be granted to the incident report approver and
@@ -3303,7 +3289,7 @@
                 allowed = true;
             }
             if (!allowed && bp.isAppPredictor()
-                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    && pkg.getPackageName().equals(mPackageManagerInt.getKnownPackageName(
                         PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
                 // Special permissions for the system app predictor.
                 allowed = true;
@@ -3326,26 +3312,27 @@
         return Boolean.TRUE == granted;
     }
 
-    private boolean isPermissionsReviewRequired(@NonNull PackageParser.Package pkg,
+    private boolean isPermissionsReviewRequired(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
         // Permission review applies only to apps not supporting the new permission model.
-        if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+        if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
             return false;
         }
 
         // Legacy apps have the permission and get user consent on launch.
-        if (pkg.mExtras == null) {
+        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
+        if (ps == null) {
             return false;
         }
-        final PackageSetting ps = (PackageSetting) pkg.mExtras;
         final PermissionsState permissionsState = ps.getPermissionsState();
         return permissionsState.isPermissionReviewRequired(userId);
     }
 
-    private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
-        final int permCount = pkg.requestedPermissions.size();
+    private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) {
+        final int permCount = pkg.getRequestedPermissions().size();
         for (int j = 0; j < permCount; j++) {
-            String requestedPermission = pkg.requestedPermissions.get(j);
+            String requestedPermission = pkg.getRequestedPermissions().get(j);
             if (permission.equals(requestedPermission)) {
                 return true;
             }
@@ -3353,41 +3340,7 @@
         return false;
     }
 
-    @GuardedBy("mLock")
-    private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
-            PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
-        if (pkg.parentPackage == null) {
-            return;
-        }
-        if (pkg.requestedPermissions == null) {
-            return;
-        }
-        final PackageParser.Package disabledPkg =
-                mPackageManagerInt.getDisabledSystemPackage(pkg.parentPackage.packageName);
-        if (disabledPkg == null || disabledPkg.mExtras == null) {
-            return;
-        }
-        final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras;
-        if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) {
-            return;
-        }
-        final int permCount = pkg.requestedPermissions.size();
-        for (int i = 0; i < permCount; i++) {
-            String permission = pkg.requestedPermissions.get(i);
-            BasePermission bp = mSettings.getPermissionLocked(permission);
-            if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
-                continue;
-            }
-            for (int userId : mUserManagerInt.getUserIds()) {
-                if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) {
-                    grantRuntimePermissionInternal(
-                            permission, pkg.packageName, false, callingUid, userId, callback);
-                }
-            }
-        }
-    }
-
-    private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+    private void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
             String[] grantedPermissions, int callingUid, PermissionCallback callback) {
         for (int userId : userIds) {
             grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid,
@@ -3395,9 +3348,10 @@
         }
     }
 
-    private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
+    private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
             String[] grantedPermissions, int callingUid, PermissionCallback callback) {
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
+        PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
+                pkg.getPackageName());
         if (ps == null) {
             return;
         }
@@ -3407,12 +3361,15 @@
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 
-        final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
+        final int compatFlags = PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
+
+        final boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
                 >= Build.VERSION_CODES.M;
 
-        final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId);
+        final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.getPackageName(), userId);
 
-        for (String permission : pkg.requestedPermissions) {
+        for (String permission : pkg.getRequestedPermissions()) {
             final BasePermission bp;
             synchronized (mLock) {
                 bp = mSettings.getPermissionLocked(permission);
@@ -3426,27 +3383,26 @@
                 if (supportsRuntimePermissions) {
                     // Installer cannot change immutable permissions.
                     if ((flags & immutableFlags) == 0) {
-                        grantRuntimePermissionInternal(permission, pkg.packageName, false,
+                        grantRuntimePermissionInternal(permission, pkg.getPackageName(), false,
                                 callingUid, userId, callback);
                     }
                 } else {
-                    // In permission review mode we clear the review flag when we
-                    // are asked to install the app with all permissions granted.
-                    if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                        updatePermissionFlagsInternal(permission, pkg.packageName,
-                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
-                                userId, false, callback);
+                    // In permission review mode we clear the review flag and the revoked compat
+                    // flag when we are asked to install the app with all permissions granted.
+                    if ((flags & compatFlags) != 0) {
+                        updatePermissionFlagsInternal(permission, pkg.getPackageName(), compatFlags,
+                                0, callingUid, userId, false, callback);
                     }
                 }
             }
         }
     }
 
-    private void setWhitelistedRestrictedPermissionsForUser(@NonNull PackageParser.Package pkg,
+    private void setWhitelistedRestrictedPermissionsForUser(@NonNull AndroidPackage pkg,
             @UserIdInt int userId, @Nullable List<String> permissions, int callingUid,
             @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
         final PermissionsState permissionsState =
-                PackageManagerServiceUtils.getPermissionsState(pkg);
+                PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
         if (permissionsState == null) {
             return;
         }
@@ -3454,9 +3410,9 @@
         ArraySet<String> oldGrantedRestrictedPermissions = null;
         boolean updatePermissions = false;
 
-        final int permissionCount = pkg.requestedPermissions.size();
+        final int permissionCount = pkg.getRequestedPermissions().size();
         for (int i = 0; i < permissionCount; i++) {
-            final String permissionName = pkg.requestedPermissions.get(i);
+            final String permissionName = pkg.getRequestedPermissions().get(i);
 
             final BasePermission bp = mSettings.getPermissionLocked(permissionName);
 
@@ -3532,19 +3488,19 @@
 
             // If we are whitelisting an app that does not support runtime permissions
             // we need to make sure it goes through the permission review UI at launch.
-            if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+            if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
                     && !wasWhitelisted && isWhitelisted) {
                 mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
                 newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
             }
 
-            updatePermissionFlagsInternal(permissionName, pkg.packageName, mask, newFlags,
+            updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
                     callingUid, userId, false, null /*callback*/);
         }
 
         if (updatePermissions) {
             // Update permission of this app to take into account the new whitelist state.
-            restorePermissionState(pkg, false, pkg.packageName, callback);
+            restorePermissionState(pkg, false, pkg.getPackageName(), callback);
 
             // If this resulted in losing a permission we need to kill the app.
             if (oldGrantedRestrictedPermissions != null) {
@@ -3553,9 +3509,9 @@
                     final String permission = oldGrantedRestrictedPermissions.valueAt(i);
                     // Sometimes we create a new permission state instance during update.
                     final PermissionsState newPermissionsState =
-                            PackageManagerServiceUtils.getPermissionsState(pkg);
+                            PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
                     if (!newPermissionsState.hasPermission(permission, userId)) {
-                        callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
+                        callback.onPermissionRevoked(pkg.getUid(), userId);
                         break;
                     }
                 }
@@ -3568,17 +3524,17 @@
             SharedUserSetting suSetting, int[] allUserIds) {
         // Collect all used permissions in the UID
         final ArraySet<String> usedPermissions = new ArraySet<>();
-        final List<PackageParser.Package> pkgList = suSetting.getPackages();
+        final List<AndroidPackage> pkgList = suSetting.getPackages();
         if (pkgList == null || pkgList.size() == 0) {
             return EmptyArray.INT;
         }
-        for (PackageParser.Package pkg : pkgList) {
-            if (pkg.requestedPermissions == null) {
+        for (AndroidPackage pkg : pkgList) {
+            if (pkg.getRequestedPermissions() == null) {
                 continue;
             }
-            final int requestedPermCount = pkg.requestedPermissions.size();
+            final int requestedPermCount = pkg.getRequestedPermissions().size();
             for (int j = 0; j < requestedPermCount; j++) {
-                String permission = pkg.requestedPermissions.get(j);
+                String permission = pkg.getRequestedPermissions().get(j);
                 BasePermission bp = mSettings.getPermissionLocked(permission);
                 if (bp != null) {
                     usedPermissions.add(permission);
@@ -3640,18 +3596,12 @@
      * @param allPackages All currently known packages
      * @param callback Callback to call after permission changes
      */
-    private void updatePermissions(@NonNull String packageName, @Nullable PackageParser.Package pkg,
+    private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg,
             @NonNull PermissionCallback callback) {
         final int flags =
                 (pkg != null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG : 0);
         updatePermissions(
                 packageName, pkg, getVolumeUuidForPackage(pkg), flags, callback);
-        if (pkg != null && pkg.childPackages != null) {
-            for (PackageParser.Package childPkg : pkg.childPackages) {
-                updatePermissions(childPkg.packageName, childPkg,
-                        getVolumeUuidForPackage(childPkg), flags, callback);
-            }
-        }
     }
 
     /**
@@ -3687,10 +3637,9 @@
                 // Only system declares background permissions, hence mapping does never change.
                 mBackgroundPermissions = new ArrayMap<>();
                 for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
-                    if (bp.perm != null && bp.perm.info != null
-                            && bp.perm.info.backgroundPermission != null) {
+                    if (bp.perm != null && bp.perm.backgroundPermission != null) {
                         String fgPerm = bp.name;
-                        String bgPerm = bp.perm.info.backgroundPermission;
+                        String bgPerm = bp.perm.backgroundPermission;
 
                         List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
                         if (fgPerms == null) {
@@ -3751,7 +3700,7 @@
      * @param callback Callback to call after permission changes
      */
     private void updatePermissions(final @Nullable String changingPkgName,
-            final @Nullable PackageParser.Package changingPkg,
+            final @Nullable AndroidPackage changingPkg,
             final @Nullable String replaceVolumeUuid,
             @UpdatePermissionFlags int flags,
             final @Nullable PermissionCallback callback) {
@@ -3784,7 +3733,7 @@
         // Now update the permissions for all packages.
         if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {
             final boolean replaceAll = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
-            mPackageManagerInt.forEachPackage((Package pkg) -> {
+            mPackageManagerInt.forEachPackage((AndroidPackage pkg) -> {
                 if (pkg == changingPkg) {
                     return;
                 }
@@ -3823,7 +3772,7 @@
      * @return {@code true} if a permission source package might have changed
      */
     private boolean updatePermissionSourcePackage(@Nullable String packageName,
-            @Nullable PackageParser.Package pkg,
+            @Nullable AndroidPackage pkg,
             final @Nullable PermissionCallback callback) {
         boolean changed = false;
 
@@ -3846,8 +3795,8 @@
                             for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
                                 final int userId = userIds[userIdNum];
 
-                                mPackageManagerInt.forEachPackage((Package p) -> {
-                                    final String pName = p.packageName;
+                                mPackageManagerInt.forEachPackage((AndroidPackage p) -> {
+                                    final String pName = p.getPackageName();
                                     final ApplicationInfo appInfo =
                                             mPackageManagerInt.getApplicationInfo(pName, 0,
                                                     Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
@@ -3892,11 +3841,13 @@
         }
         if (needsUpdate != null) {
             for (final BasePermission bp : needsUpdate) {
-                final PackageParser.Package sourcePkg =
+                final AndroidPackage sourcePkg =
                         mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                final PackageSetting sourcePs =
+                        (PackageSetting) mPackageManagerInt.getPackageSetting(
+                                bp.getSourcePackageName());
                 synchronized (mLock) {
-                    if (sourcePkg != null && sourcePkg.mExtras != null) {
-                        final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
+                    if (sourcePkg != null && sourcePs != null) {
                         if (bp.getSourcePackageSetting() == null) {
                             bp.setSourcePackageSetting(sourcePs);
                         }
@@ -3929,7 +3880,7 @@
      * @return {@code true} if a permission tree ownership might have changed
      */
     private boolean updatePermissionTreeSourcePackage(@Nullable String packageName,
-            @Nullable PackageParser.Package pkg) {
+            @Nullable AndroidPackage pkg) {
         boolean changed = false;
 
         Set<BasePermission> needsUpdate = null;
@@ -3955,11 +3906,13 @@
         }
         if (needsUpdate != null) {
             for (final BasePermission bp : needsUpdate) {
-                final PackageParser.Package sourcePkg =
+                final AndroidPackage sourcePkg =
                         mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                final PackageSetting sourcePs =
+                        (PackageSetting) mPackageManagerInt.getPackageSetting(
+                                bp.getSourcePackageName());
                 synchronized (mLock) {
-                    if (sourcePkg != null && sourcePkg.mExtras != null) {
-                        final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
+                    if (sourcePkg != null && sourcePs != null) {
                         if (bp.getSourcePackageSetting() == null) {
                             bp.setSourcePackageSetting(sourcePs);
                         }
@@ -4082,24 +4035,28 @@
         }
     }
 
-    private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
+    private static String getVolumeUuidForPackage(AndroidPackage pkg) {
         if (pkg == null) {
             return StorageManager.UUID_PRIVATE_INTERNAL;
         }
         if (pkg.isExternal()) {
-            if (TextUtils.isEmpty(pkg.volumeUuid)) {
+            if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
                 return StorageManager.UUID_PRIMARY_PHYSICAL;
             } else {
-                return pkg.volumeUuid;
+                return pkg.getVolumeUuid();
             }
         } else {
             return StorageManager.UUID_PRIVATE_INTERNAL;
         }
     }
 
-    private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
-        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
-            if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
+    private static boolean hasPermission(AndroidPackage pkg, String permName) {
+        if (pkg.getPermissions() == null) {
+            return false;
+        }
+
+        for (int i = pkg.getPermissions().size() - 1; i >= 0; i--) {
+            if (pkg.getPermissions().get(i).getName().equals(permName)) {
                 return true;
             }
         }
@@ -4138,37 +4095,39 @@
             PermissionManagerService.this.systemReady();
         }
         @Override
-        public boolean isPermissionsReviewRequired(@NonNull Package pkg, @UserIdInt int userId) {
+        public boolean isPermissionsReviewRequired(@NonNull AndroidPackage pkg,
+                @UserIdInt int userId) {
             return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
         }
+
         @Override
         public void revokeRuntimePermissionsIfGroupChanged(
-                @NonNull PackageParser.Package newPackage,
-                @NonNull PackageParser.Package oldPackage,
+                @NonNull AndroidPackage newPackage,
+                @NonNull AndroidPackage oldPackage,
                 @NonNull ArrayList<String> allPackageNames) {
             PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
                     oldPackage, allPackageNames, mDefaultPermissionCallback);
         }
         @Override
-        public void addAllPermissions(Package pkg, boolean chatty) {
+        public void addAllPermissions(AndroidPackage pkg, boolean chatty) {
             PermissionManagerService.this.addAllPermissions(pkg, chatty);
         }
         @Override
-        public void addAllPermissionGroups(Package pkg, boolean chatty) {
+        public void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) {
             PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
         }
         @Override
-        public void removeAllPermissions(Package pkg, boolean chatty) {
+        public void removeAllPermissions(AndroidPackage pkg, boolean chatty) {
             PermissionManagerService.this.removeAllPermissions(pkg, chatty);
         }
         @Override
-        public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
+        public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
                 String[] grantedPermissions, int callingUid) {
             PermissionManagerService.this.grantRequestedRuntimePermissions(
                     pkg, userIds, grantedPermissions, callingUid, mDefaultPermissionCallback);
         }
         @Override
-        public void setWhitelistedRestrictedPermissions(@NonNull PackageParser.Package pkg,
+        public void setWhitelistedRestrictedPermissions(@NonNull AndroidPackage pkg,
                 @NonNull int[] userIds, @Nullable List<String> permissions, int callingUid,
                 @PackageManager.PermissionWhitelistFlags int flags) {
             for (int userId : userIds) {
@@ -4183,13 +4142,7 @@
                     packageName, permissions, flags, userId);
         }
         @Override
-        public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg,
-                int callingUid) {
-            PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked(
-                    pkg, callingUid, mDefaultPermissionCallback);
-        }
-        @Override
-        public void updatePermissions(@NonNull String packageName, @Nullable Package pkg) {
+        public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
             PermissionManagerService.this
                     .updatePermissions(packageName, pkg, mDefaultPermissionCallback);
         }
@@ -4199,13 +4152,13 @@
                     .updateAllPermissions(volumeUuid, sdkUpdated, mDefaultPermissionCallback);
         }
         @Override
-        public void resetRuntimePermissions(Package pkg, int userId) {
+        public void resetRuntimePermissions(AndroidPackage pkg, int userId) {
             PermissionManagerService.this.resetRuntimePermissionsInternal(pkg, userId);
         }
         @Override
         public void resetAllRuntimePermissions(final int userId) {
             mPackageManagerInt.forEachPackage(
-                    (PackageParser.Package pkg) -> resetRuntimePermissionsInternal(pkg, userId));
+                    (AndroidPackage pkg) -> resetRuntimePermissionsInternal(pkg, userId));
         }
         @Override
         public String[] getAppOpPermissionPackages(String permName, int callingUid) {
@@ -4251,9 +4204,9 @@
                 for (int i = 0; i < numTotalPermissions; i++) {
                     BasePermission bp = mSettings.mPermissions.valueAt(i);
 
-                    if (bp.perm != null && bp.perm.info != null
-                            && bp.protectionLevel == protectionLevel) {
-                        matchingPermissions.add(bp.perm.info);
+                    if (bp.perm != null && bp.protectionLevel == protectionLevel) {
+                        matchingPermissions.add(
+                                PackageInfoUtils.generatePermissionInfo(bp.perm, 0));
                     }
                 }
             }
@@ -4334,15 +4287,17 @@
 
         @Override
         public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) {
-            synchronized (mLock) {
-                if (userId == UserHandle.USER_ALL) {
-                    return;
-                }
-                if (mDefaultHomeProvider == null) {
-                    return;
-                }
-                mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId, callback);
+            if (userId == UserHandle.USER_ALL) {
+                return;
             }
+            DefaultHomeProvider provider;
+            synchronized (mLock) {
+                provider = mDefaultHomeProvider;
+            }
+            if (provider == null) {
+                return;
+            }
+            provider.setDefaultHomeAsync(packageName, userId, callback);
         }
 
         @Override
@@ -4403,26 +4358,29 @@
 
         @Override
         public String getDefaultBrowser(int userId) {
+            DefaultBrowserProvider provider;
             synchronized (mLock) {
-                return mDefaultBrowserProvider == null
-                        ? null : mDefaultBrowserProvider.getDefaultBrowser(userId);
+                provider = mDefaultBrowserProvider;
             }
+            return provider != null ? provider.getDefaultBrowser(userId) : null;
         }
 
         @Override
         public String getDefaultDialer(int userId) {
+            DefaultDialerProvider provider;
             synchronized (mLock) {
-                return mDefaultDialerProvider == null
-                        ? null : mDefaultDialerProvider.getDefaultDialer(userId);
+                provider = mDefaultDialerProvider;
             }
+            return provider != null ? provider.getDefaultDialer(userId) : null;
         }
 
         @Override
         public String getDefaultHome(int userId) {
+            DefaultHomeProvider provider;
             synchronized (mLock) {
-                return mDefaultHomeProvider == null
-                        ? null : mDefaultHomeProvider.getDefaultHome(userId);
+                provider = mDefaultHomeProvider;
             }
+            return provider != null ? provider.getDefaultHome(userId) : null;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 8f22f92..752c2dc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -21,8 +21,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.permission.PermissionManagerInternal;
 
 import java.util.ArrayList;
@@ -173,16 +173,14 @@
 
     public abstract void systemReady();
 
-    public abstract boolean isPermissionsReviewRequired(@NonNull PackageParser.Package pkg,
+    public abstract boolean isPermissionsReviewRequired(@NonNull AndroidPackage pkg,
             @UserIdInt int userId);
 
-    public abstract void grantRuntimePermissionsGrantedToDisabledPackage(
-            @NonNull PackageParser.Package pkg, int callingUid);
     public abstract void grantRequestedRuntimePermissions(
-            @NonNull PackageParser.Package pkg, @NonNull int[] userIds,
+            @NonNull AndroidPackage pkg, @NonNull int[] userIds,
             @NonNull String[] grantedPermissions, int callingUid);
     public abstract void setWhitelistedRestrictedPermissions(
-            @NonNull PackageParser.Package pkg, @NonNull int[] userIds,
+            @NonNull AndroidPackage pkg, @NonNull int[] userIds,
             @NonNull List<String> permissions, int callingUid,
             @PackageManager.PermissionWhitelistFlags int whitelistFlags);
     /** Sets the whitelisted, restricted permissions for the given package. */
@@ -204,7 +202,7 @@
      * @param callback Callback to call after permission changes
      */
     public abstract void updatePermissions(@NonNull String packageName,
-            @Nullable PackageParser.Package pkg);
+            @Nullable AndroidPackage pkg);
 
     /**
      * Update all permissions for all apps.
@@ -224,7 +222,7 @@
      * Resets any user permission state changes (eg. permissions and flags) of all
      * packages installed for the given user.
      *
-     * @see #resetRuntimePermissions(android.content.pm.PackageParser.Package, int)
+     * @see #resetRuntimePermissions(AndroidPackage, int)
      */
     public abstract void resetAllRuntimePermissions(@UserIdInt int userId);
 
@@ -232,7 +230,7 @@
      * Resets any user permission state changes (eg. permissions and flags) of the
      * specified package for the given user.
      */
-    public abstract void resetRuntimePermissions(@NonNull PackageParser.Package pkg,
+    public abstract void resetRuntimePermissions(@NonNull AndroidPackage pkg,
             @UserIdInt int userId);
 
     /**
@@ -245,8 +243,8 @@
      * @param allPackageNames All packages
      */
     public abstract void revokeRuntimePermissionsIfGroupChanged(
-            @NonNull PackageParser.Package newPackage,
-            @NonNull PackageParser.Package oldPackage,
+            @NonNull AndroidPackage newPackage,
+            @NonNull AndroidPackage oldPackage,
             @NonNull ArrayList<String> allPackageNames);
 
     /**
@@ -255,9 +253,9 @@
      * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
      * the permission settings.
      */
-    public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
-    public abstract void addAllPermissionGroups(@NonNull PackageParser.Package pkg, boolean chatty);
-    public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract void addAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
+    public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty);
+    public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
 
     /** Retrieve the packages that have requested the given app op permission */
     public abstract @Nullable String[] getAppOpPermissionPackages(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 3d8cf2d..254b720 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ComponentParseUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -65,8 +65,8 @@
      * name to permission group object.
      */
     @GuardedBy("mLock")
-    final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups =
-            new ArrayMap<String, PackageParser.PermissionGroup>();
+    final ArrayMap<String, ComponentParseUtils.ParsedPermissionGroup> mPermissionGroups =
+            new ArrayMap<>();
 
     /**
      * Set of packages that request a particular app op. The mapping is from permission
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 505a0e2..a78f5c4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -23,6 +23,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
@@ -30,7 +31,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import com.android.internal.annotations.GuardedBy;
 
 /**
  * This class encapsulates the permissions for a package or a shared user.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 1bf319d..2f66713 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -17,13 +17,12 @@
 package com.android.server.policy;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 
 import android.annotation.NonNull;
@@ -38,8 +37,8 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
-import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
 import android.os.Process;
 import android.os.RemoteException;
@@ -157,13 +156,12 @@
                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
                 } else if (perm.isSoftRestricted()) {
                     appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
-
                     SoftRestrictedPermissionPolicy policy =
                             SoftRestrictedPermissionPolicy.forPermission(null, null, null,
                                     perm.name);
-                    if (policy.resolveAppOp() != OP_NONE) {
-                        appOpsService.startWatchingMode(policy.resolveAppOp(), null,
-                                appOpsListener);
+                    int extraAppOp = policy.getExtraAppOpCode();
+                    if (extraAppOp != OP_NONE) {
+                        appOpsService.startWatchingMode(extraAppOp, null, appOpsListener);
                     }
                 }
             }
@@ -358,10 +356,10 @@
                 pkg.sharedUserId, userId);
         if (sharedPkgNames != null) {
             for (String sharedPkgName : sharedPkgNames) {
-                final PackageParser.Package sharedPkg = packageManagerInternal
+                final AndroidPackage sharedPkg = packageManagerInternal
                         .getPackage(sharedPkgName);
                 if (sharedPkg != null) {
-                    synchroniser.addPackage(sharedPkg.packageName);
+                    synchroniser.addPackage(sharedPkg.getPackageName());
                 }
             }
         }
@@ -378,7 +376,8 @@
                 PackageManagerInternal.class);
         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
                 getUserContext(getContext(), UserHandle.of(userId)));
-        packageManagerInternal.forEachPackage((pkg) -> synchronizer.addPackage(pkg.packageName));
+        packageManagerInternal.forEachPackage(
+                (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
         synchronizer.syncPackages();
     }
 
@@ -395,24 +394,6 @@
         private final @NonNull SparseIntArray mAllUids = new SparseIntArray();
 
         /**
-         * All ops that need to be set to default
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToDefault = new ArrayList<>();
-
-        /**
-         * All ops that need to be flipped to allow if default.
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToAllowIfDefault = new ArrayList<>();
-
-        /**
          * All ops that need to be flipped to allow.
          *
          * @see #syncPackages
@@ -420,15 +401,6 @@
         private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
 
         /**
-         * All ops that need to be flipped to ignore if default.
-         *
-         * Currently, only used by the restricted permissions logic.
-         *
-         * @see #syncPackages
-         */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfDefault = new ArrayList<>();
-
-        /**
          * All ops that need to be flipped to ignore.
          *
          * @see #syncPackages
@@ -436,6 +408,15 @@
         private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
 
         /**
+         * All ops that need to be flipped to ignore if not allowed.
+         *
+         * Currently, only used by soft restricted permissions logic.
+         *
+         * @see #syncPackages
+         */
+        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
+
+        /**
          * All ops that need to be flipped to foreground.
          *
          * Currently, only used by the foreground/background permissions logic.
@@ -479,19 +460,6 @@
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
-            final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
-            for (int i = 0; i < allowIfDefaultCount; i++) {
-                final OpToChange op = mOpsToAllowIfDefault.get(i);
-                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
-                    continue;
-                }
-
-                boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
-                if (wasSet) {
-                    alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
-                }
-            }
-
             final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size();
             for (int i = 0; i < foregroundIfAllowedCount; i++) {
                 final OpToChange op = mOpsToForegroundIfAllow.get(i);
@@ -527,29 +495,18 @@
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
-            final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
-            for (int i = 0; i < ignoreIfDefaultCount; i++) {
-                final OpToChange op = mOpsToIgnoreIfDefault.get(i);
+            final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
+            for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
+                final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
                 if (wasSet) {
                     alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
                 }
             }
-
-            final int defaultCount = mOpsToDefault.size();
-            for (int i = 0; i < defaultCount; i++) {
-                final OpToChange op = mOpsToDefault.get(i);
-                if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
-                    continue;
-                }
-
-                setUidModeDefault(op.code, op.uid, op.packageName);
-                alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
-            }
         }
 
         /**
@@ -571,60 +528,49 @@
                 return;
             }
 
-            final boolean applyRestriction =
-                    (mPackageManager.getPermissionFlags(permission, pkg.packageName,
-                    mContext.getUser()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-
-            if (permissionInfo.isHardRestricted()) {
-                if (opCode != OP_NONE) {
-                    if (applyRestriction) {
-                        mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
-                    } else {
-                        mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
+            if (opCode != OP_NONE) {
+                int permissionFlags = mPackageManager.getPermissionFlags(permission,
+                        pkg.packageName, mContext.getUser());
+                boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
+                if (!isReviewRequired) {
+                    boolean isRevokedCompat =
+                            (permissionFlags & FLAG_PERMISSION_REVOKED_COMPAT) != 0;
+                    if (permissionInfo.isHardRestricted()) {
+                        boolean shouldApplyRestriction =
+                                (permissionFlags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                        if (isRevokedCompat || shouldApplyRestriction) {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, opCode));
+                        } else {
+                            mOpsToAllow.add(new OpToChange(uid, pkg.packageName, opCode));
+                        }
+                    } else if (permissionInfo.isSoftRestricted()) {
+                        SoftRestrictedPermissionPolicy policy =
+                                SoftRestrictedPermissionPolicy.forPermission(mContext,
+                                        pkg.applicationInfo, mContext.getUser(), permission);
+                        if (!isRevokedCompat && policy.mayGrantPermission()) {
+                            mOpsToAllow.add(new OpToChange(uid, pkg.packageName, opCode));
+                        } else {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, opCode));
+                        }
                     }
                 }
-            } else if (permissionInfo.isSoftRestricted()) {
-                final SoftRestrictedPermissionPolicy policy =
+            }
+
+            if (permissionInfo.isSoftRestricted()) {
+                SoftRestrictedPermissionPolicy policy =
                         SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
                                 mContext.getUser(), permission);
-
-                if (opCode != OP_NONE) {
-                    if (policy.canBeGranted()) {
-                        mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
+                int extraOpCode = policy.getExtraAppOpCode();
+                if (extraOpCode != OP_NONE) {
+                    if (policy.mayAllowExtraAppOp()) {
+                        mOpsToAllow.add(new OpToChange(uid, pkg.packageName, extraOpCode));
                     } else {
-                        mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
-                    }
-                }
-
-                final int op = policy.resolveAppOp();
-                if (op != OP_NONE) {
-                    switch (policy.getDesiredOpMode()) {
-                        case MODE_DEFAULT:
-                            mOpsToDefault.add(new OpToChange(uid, pkg.packageName, op));
-                            break;
-                        case MODE_ALLOWED:
-                            if (policy.shouldSetAppOpIfNotDefault()) {
-                                mOpsToAllow.add(new OpToChange(uid, pkg.packageName, op));
-                            } else {
-                                mOpsToAllowIfDefault.add(
-                                        new OpToChange(uid, pkg.packageName, op));
-                            }
-                            break;
-                        case MODE_FOREGROUND:
-                            Slog.wtf(LOG_TAG,
-                                    "Setting appop to foreground is not implemented");
-                            break;
-                        case MODE_IGNORED:
-                            if (policy.shouldSetAppOpIfNotDefault()) {
-                                mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, op));
-                            } else {
-                                mOpsToIgnoreIfDefault.add(
-                                        new OpToChange(uid, pkg.packageName,
-                                                op));
-                            }
-                            break;
-                        case MODE_ERRORED:
-                            Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
+                        if (policy.mayDenyExtraAppOpIfGranted()) {
+                            mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, extraOpCode));
+                        } else {
+                            mOpsToIgnoreIfNotAllowed.add(new OpToChange(uid, pkg.packageName,
+                                    extraOpCode));
+                        }
                     }
                 }
             }
@@ -743,60 +689,51 @@
             }
         }
 
-        private boolean setUidModeAllowedIfDefault(int opCode, int uid,
-                @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
-        }
-
         private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_ALLOWED, packageName);
         }
 
         private boolean setUidModeForegroundIfAllow(int opCode, int uid,
                 @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
+            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                    opCode), uid, packageName);
+            if (currentMode == MODE_ALLOWED) {
+                mAppOpsManager.setUidMode(opCode, uid, MODE_FOREGROUND);
+                return true;
+            }
+            return false;
         }
 
         private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
         }
 
-        private boolean setUidModeIgnoredIfDefault(int opCode, int uid,
-                @NonNull String packageName) {
-            return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
-        }
-
         private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
             setUidMode(opCode, uid, MODE_IGNORED, packageName);
         }
 
+        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
+                @NonNull String packageName) {
+            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+                    opCode), uid, packageName);
+            if (currentMode != MODE_ALLOWED) {
+                if (currentMode != MODE_IGNORED) {
+                    mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+                }
+                return true;
+            }
+            return false;
+        }
+
         private void setUidMode(int opCode, int uid, int mode,
                 @NonNull String packageName) {
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
                     .opToPublicName(opCode), uid, packageName);
-
             if (currentMode != mode) {
                 mAppOpsManager.setUidMode(opCode, uid, mode);
             }
         }
 
-        private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
-                @NonNull String packageName) {
-            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
-                    .opToPublicName(opCode), uid, packageName);
-
-            if (currentMode == requiredModeBefore) {
-                mAppOpsManager.setUidMode(opCode, uid, newMode);
-                return true;
-            }
-
-            return false;
-        }
-
-        private void setUidModeDefault(int opCode, int uid, String packageName) {
-            setUidMode(opCode, uid, MODE_DEFAULT, packageName);
-        }
-
         private class OpToChange {
             final int uid;
             final @NonNull String packageName;
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index c1a6dbd..b0f22e4 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -18,9 +18,6 @@
 
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
@@ -55,22 +52,7 @@
     private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
             new SoftRestrictedPermissionPolicy() {
                 @Override
-                public int resolveAppOp() {
-                    return OP_NONE;
-                }
-
-                @Override
-                public int getDesiredOpMode() {
-                    return MODE_DEFAULT;
-                }
-
-                @Override
-                public boolean shouldSetAppOpIfNotDefault() {
-                    return false;
-                }
-
-                @Override
-                public boolean canBeGranted() {
+                public boolean mayGrantPermission() {
                     return true;
                 }
             };
@@ -114,10 +96,8 @@
      * Get the policy for a soft restricted permission.
      *
      * @param context A context to use
-     * @param appInfo The application the permission belongs to. Can be {@code null}, but then
-     *                only {@link #resolveAppOp} will work.
-     * @param user The user the app belongs to. Can be {@code null}, but then only
-     *             {@link #resolveAppOp} will work.
+     * @param appInfo The application the permission belongs to.
+     * @param user The user the app belongs to.
      * @param permission The name of the permission
      *
      * @return The policy for this permission
@@ -130,82 +110,46 @@
             // where the restricted state allows the permission but only for accessing the medial
             // collections.
             case READ_EXTERNAL_STORAGE: {
-                final int flags;
-                final boolean applyRestriction;
                 final boolean isWhiteListed;
-                final boolean hasRequestedLegacyExternalStorage;
+                boolean shouldApplyRestriction;
                 final int targetSDK;
+                final boolean hasRequestedLegacyExternalStorage;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
-                    flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
-                    applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+                    int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+                    shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
                     targetSDK = getMinimumTargetSDK(context, appInfo, user);
-
-                    boolean hasAnyRequestedLegacyExternalStorage =
-                            appInfo.hasRequestedLegacyExternalStorage();
-
-                    // hasRequestedLegacyExternalStorage is per package. To make sure two apps in
-                    // the same shared UID do not fight over what to set, always compute the
-                    // combined hasRequestedLegacyExternalStorage
-                    String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
-                    if (uidPkgs != null) {
-                        for (String uidPkg : uidPkgs) {
-                            if (!uidPkg.equals(appInfo.packageName)) {
-                                ApplicationInfo uidPkgInfo;
-                                try {
-                                    uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
-                                } catch (PackageManager.NameNotFoundException e) {
-                                    continue;
-                                }
-
-                                hasAnyRequestedLegacyExternalStorage |=
-                                        uidPkgInfo.hasRequestedLegacyExternalStorage();
-                            }
-                        }
-                    }
-
-                    hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;
+                    hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
+                            appInfo.uid, context);
                 } else {
-                    flags = 0;
-                    applyRestriction = false;
                     isWhiteListed = false;
-                    hasRequestedLegacyExternalStorage = false;
+                    shouldApplyRestriction = false;
                     targetSDK = 0;
+                    hasRequestedLegacyExternalStorage = false;
                 }
 
+                // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
+                // to prevent apps losing files in legacy storage, because we are holding the
+                // package manager lock here. If we ever remove this policy that check should be
+                // removed as well.
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
-                    public int resolveAppOp() {
+                    public boolean mayGrantPermission() {
+                        return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+                    }
+                    @Override
+                    public int getExtraAppOpCode() {
                         return OP_LEGACY_STORAGE;
                     }
-
                     @Override
-                    public int getDesiredOpMode() {
-                        if (applyRestriction) {
-                            return MODE_DEFAULT;
-                        } else if (hasRequestedLegacyExternalStorage) {
-                            return MODE_ALLOWED;
-                        } else {
-                            return MODE_IGNORED;
-                        }
+                    public boolean mayAllowExtraAppOp() {
+                        return !shouldApplyRestriction && hasRequestedLegacyExternalStorage;
                     }
-
                     @Override
-                    public boolean shouldSetAppOpIfNotDefault() {
-                        // Do not switch from allowed -> ignored as this would mean to retroactively
-                        // turn on isolated storage. This will make the app loose all its files.
-                        return getDesiredOpMode() != MODE_IGNORED;
-                    }
-
-                    @Override
-                    public boolean canBeGranted() {
-                        if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
-                            return true;
-                        } else {
-                            return false;
-                        }
+                    public boolean mayDenyExtraAppOpIfGranted() {
+                        return shouldApplyRestriction;
                     }
                 };
             }
@@ -225,22 +169,7 @@
 
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
-                    public int resolveAppOp() {
-                        return OP_NONE;
-                    }
-
-                    @Override
-                    public int getDesiredOpMode() {
-                        return MODE_DEFAULT;
-                    }
-
-                    @Override
-                    public boolean shouldSetAppOpIfNotDefault() {
-                        return false;
-                    }
-
-                    @Override
-                    public boolean canBeGranted() {
+                    public boolean mayGrantPermission() {
                         return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
                     }
                 };
@@ -250,25 +179,51 @@
         }
     }
 
-    /**
-     * @return An app op to be changed based on the state of the permission or
-     * {@link AppOpsManager#OP_NONE} if not app-op should be set.
-     */
-    public abstract int resolveAppOp();
-
-    /**
-     * @return The mode the {@link #resolveAppOp() app op} should be in.
-     */
-    public abstract @AppOpsManager.Mode int getDesiredOpMode();
-
-    /**
-     * @return If the {@link #resolveAppOp() app op} should be set even if the app-op is currently
-     * not {@link AppOpsManager#MODE_DEFAULT}.
-     */
-    public abstract boolean shouldSetAppOpIfNotDefault();
+    private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        String[] packageNames = packageManager.getPackagesForUid(uid);
+        if (packageNames == null) {
+            return false;
+        }
+        UserHandle user = UserHandle.getUserHandleForUid(uid);
+        for (String packageName : packageNames) {
+            ApplicationInfo applicationInfo;
+            try {
+                applicationInfo = packageManager.getApplicationInfoAsUser(packageName, 0, user);
+            } catch (PackageManager.NameNotFoundException e) {
+                continue;
+            }
+            if (applicationInfo.hasRequestedLegacyExternalStorage()) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     /**
      * @return If the permission can be granted
      */
-    public abstract boolean canBeGranted();
+    public abstract boolean mayGrantPermission();
+
+    /**
+     * @return An app op to be changed based on the state of the permission or
+     * {@link AppOpsManager#OP_NONE} if not app-op should be set.
+     */
+    public int getExtraAppOpCode() {
+        return OP_NONE;
+    }
+
+    /**
+     * @return Whether the {@link #getExtraAppOpCode() app op} may be granted.
+     */
+    public boolean mayAllowExtraAppOp() {
+        return false;
+    }
+
+    /**
+     * @return Whether the {@link #getExtraAppOpCode() app op} may be denied if was granted.
+     */
+    public boolean mayDenyExtraAppOpIfGranted() {
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index bf8c042..0687635 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -308,12 +308,12 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
 
         pm.forEachInstalledPackage(FunctionalUtils.uncheckExceptions(pkg -> {
-            out.write(pkg.packageName.getBytes());
+            out.write(pkg.getPackageName().getBytes());
             out.write(BitUtils.toBytes(pkg.getLongVersionCode()));
-            out.write(pm.getApplicationEnabledState(pkg.packageName, userId));
+            out.write(pm.getApplicationEnabledState(pkg.getPackageName(), userId));
 
             ArraySet<String> enabledComponents =
-                    pm.getEnabledComponents(pkg.packageName, userId);
+                    pm.getEnabledComponents(pkg.getPackageName(), userId);
             int numComponents = CollectionUtils.size(enabledComponents);
             out.write(numComponents);
             for (int i = 0; i < numComponents; i++) {
@@ -321,12 +321,12 @@
             }
 
             ArraySet<String> disabledComponents =
-                    pm.getDisabledComponents(pkg.packageName, userId);
+                    pm.getDisabledComponents(pkg.getPackageName(), userId);
             numComponents = CollectionUtils.size(disabledComponents);
             for (int i = 0; i < numComponents; i++) {
                 out.write(disabledComponents.valueAt(i).getBytes());
             }
-            for (Signature signature : pkg.mSigningDetails.signatures) {
+            for (Signature signature : pkg.getSigningDetails().signatures) {
                 out.write(signature.toByteArray());
             }
         }), userId);
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 3f9cc83..a9f44b9 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -78,7 +78,6 @@
                         + packageRollbackInfo.getPackageName() + ", userId: " + user, ie);
             }
         }
-        packageRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
     }
 
     /**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 2dc4951..78b8b2e 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -16,14 +16,33 @@
 
 package com.android.server.rollback;
 
+import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure;
+
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseLongArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.ParseException;
@@ -41,10 +60,14 @@
  * {@link PackageRollbackInfo} objects held within.
  */
 class Rollback {
+
+    private static final String TAG = "RollbackManager";
+
     @IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
             ROLLBACK_STATE_ENABLING,
             ROLLBACK_STATE_AVAILABLE,
             ROLLBACK_STATE_COMMITTED,
+            ROLLBACK_STATE_DELETED
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface RollbackState {}
@@ -66,6 +89,11 @@
     static final int ROLLBACK_STATE_COMMITTED = 3;
 
     /**
+     * The rollback has been deleted.
+     */
+    static final int ROLLBACK_STATE_DELETED = 4;
+
+    /**
      * The session ID for the staged session if this rollback data represents a staged session,
      * {@code -1} otherwise.
      */
@@ -234,19 +262,285 @@
     }
 
     /**
-     * Sets the state of the rollback to AVAILABLE.
+     * Returns true if the rollback is in the DELETED state.
      */
     @GuardedBy("getLock")
-    void setAvailable() {
-        mState = ROLLBACK_STATE_AVAILABLE;
+    boolean isDeleted() {
+        return mState == ROLLBACK_STATE_DELETED;
     }
 
     /**
-     * Sets the state of the rollback to COMMITTED.
+     * Enables this rollback for the provided package.
+     *
+     * @return boolean True if the rollback was enabled successfully for the specified package.
      */
     @GuardedBy("getLock")
-    void setCommitted() {
-        mState = ROLLBACK_STATE_COMMITTED;
+    boolean enableForPackage(String packageName, long newVersion, long installedVersion,
+            boolean isApex, String sourceDir, String[] splitSourceDirs) {
+        try {
+            RollbackStore.backupPackageCodePath(this, packageName, sourceDir);
+            if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+                for (String dir : splitSourceDirs) {
+                    RollbackStore.backupPackageCodePath(this, packageName, dir);
+                }
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
+            return false;
+        }
+
+        PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
+                new VersionedPackage(packageName, newVersion),
+                new VersionedPackage(packageName, installedVersion),
+                new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
+                isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
+
+        info.getPackages().add(packageRollbackInfo);
+
+        return true;
+    }
+
+    /**
+     * Snapshots user data for the provided package and user ids. Does nothing if this rollback is
+     * not in the ENABLING state.
+     */
+    @GuardedBy("getLock")
+    void snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper) {
+        if (!isEnabling()) {
+            return;
+        }
+
+        for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+            if (pkgRollbackInfo.getPackageName().equals(packageName)) {
+                dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
+
+                RollbackStore.saveRollback(this);
+                pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
+                break;
+            }
+        }
+    }
+
+    /**
+     * Changes the state of the rollback to AVAILABLE. This also changes the timestamp to the
+     * current time and saves the rollback. Does nothing if this rollback is already in the
+     * DELETED state.
+     */
+    @GuardedBy("getLock")
+    void makeAvailable() {
+        if (isDeleted()) {
+            Slog.w(TAG, "Cannot make deleted rollback available.");
+            return;
+        }
+        mState = ROLLBACK_STATE_AVAILABLE;
+        mTimestamp = Instant.now();
+        RollbackStore.saveRollback(this);
+    }
+
+    /**
+     * Commits the rollback.
+     */
+    @GuardedBy("getLock")
+    void commit(final Context context, List<VersionedPackage> causePackages,
+            String callerPackageName, IntentSender statusReceiver) {
+
+        if (!isAvailable()) {
+            sendFailure(context, statusReceiver,
+                    RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+                    "Rollback unavailable");
+            return;
+        }
+
+        // Get a context to use to install the downgraded version of the package.
+        Context pkgContext;
+        try {
+            pkgContext = context.createPackageContext(callerPackageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+                    "Invalid callerPackageName");
+            return;
+        }
+
+        PackageManager pm = pkgContext.getPackageManager();
+        try {
+            PackageInstaller packageInstaller = pm.getPackageInstaller();
+            PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
+                    PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            parentParams.setRequestDowngrade(true);
+            parentParams.setMultiPackage();
+            if (isStaged()) {
+                parentParams.setStaged();
+            }
+
+            int parentSessionId = packageInstaller.createSession(parentParams);
+            PackageInstaller.Session parentSession = packageInstaller.openSession(
+                    parentSessionId);
+
+            for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+                // TODO: We can't get the installerPackageName for apex
+                // (b/123920130). Is it okay to ignore the installer package
+                // for apex?
+                if (!pkgRollbackInfo.isApex()) {
+                    String installerPackageName =
+                            pm.getInstallerPackageName(pkgRollbackInfo.getPackageName());
+                    if (installerPackageName != null) {
+                        params.setInstallerPackageName(installerPackageName);
+                    }
+                }
+                params.setRequestDowngrade(true);
+                params.setRequiredInstalledVersionCode(
+                        pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
+                if (isStaged()) {
+                    params.setStaged();
+                }
+                if (pkgRollbackInfo.isApex()) {
+                    params.setInstallAsApex();
+                }
+                int sessionId = packageInstaller.createSession(params);
+                PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+                File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+                        this, pkgRollbackInfo.getPackageName());
+                if (packageCodePaths == null) {
+                    sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+                            "Backup copy of package inaccessible");
+                    return;
+                }
+
+                for (File packageCodePath : packageCodePaths) {
+                    try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+                            ParcelFileDescriptor.MODE_READ_ONLY)) {
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            session.write(packageCodePath.getName(), 0,
+                                    packageCodePath.length(),
+                                    fd);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                }
+                parentSession.addChildSessionId(sessionId);
+            }
+
+            final LocalIntentReceiver receiver = new LocalIntentReceiver(
+                    (Intent result) -> {
+                        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            // Committing the rollback failed, but we still have all the info we
+                            // need to try rolling back again, so restore the rollback state to how
+                            // it was before we tried committing.
+                            // TODO: Should we just kill this rollback if commit failed?
+                            // Why would we expect commit not to fail again?
+                            // TODO: Could this cause a rollback to be resurrected
+                            // if it should otherwise have expired by now?
+                            synchronized (mLock) {
+                                mState = ROLLBACK_STATE_AVAILABLE;
+                                mRestoreUserDataInProgress = false;
+                            }
+                            sendFailure(context, statusReceiver,
+                                    RollbackManager.STATUS_FAILURE_INSTALL,
+                                    "Rollback downgrade install failed: "
+                                            + result.getStringExtra(
+                                            PackageInstaller.EXTRA_STATUS_MESSAGE));
+                            return;
+                        }
+
+                        synchronized (mLock) {
+                            if (!isStaged()) {
+                                // All calls to restoreUserData should have
+                                // completed by now for a non-staged install.
+                                mRestoreUserDataInProgress = false;
+                            }
+
+                            info.setCommittedSessionId(parentSessionId);
+                            info.getCausePackages().addAll(causePackages);
+                            RollbackStore.deletePackageCodePaths(this);
+                            RollbackStore.saveRollback(this);
+                        }
+
+                        // Send success.
+                        try {
+                            final Intent fillIn = new Intent();
+                            fillIn.putExtra(
+                                    RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS);
+                            statusReceiver.sendIntent(context, 0, fillIn, null, null);
+                        } catch (IntentSender.SendIntentException e) {
+                            // Nowhere to send the result back to, so don't bother.
+                        }
+
+                        Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
+
+                        for (UserInfo userInfo : UserManager.get(context).getUsers(true)) {
+                            context.sendBroadcastAsUser(broadcast,
+                                    userInfo.getUserHandle(),
+                                    Manifest.permission.MANAGE_ROLLBACKS);
+                        }
+                    }
+            );
+
+            mState = ROLLBACK_STATE_COMMITTED;
+            mRestoreUserDataInProgress = true;
+            parentSession.commit(receiver.getIntentSender());
+        } catch (IOException e) {
+            Slog.e(TAG, "Rollback failed", e);
+            sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+                    "IOException: " + e.toString());
+        }
+    }
+
+    /**
+     * Restores user data for the specified package if this rollback is currently marked as
+     * having a restore in progress.
+     *
+     * @return boolean True if this rollback has a restore in progress and contains the specified
+     * package.
+     */
+    @GuardedBy("getLock")
+    boolean restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId,
+            String seInfo, AppDataRollbackHelper dataHelper) {
+        if (!isRestoreUserDataInProgress()) {
+            return false;
+        }
+
+        boolean foundPackage = false;
+        for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+            if (pkgRollbackInfo.getPackageName().equals(packageName)) {
+                foundPackage = true;
+                boolean changedRollback = false;
+                for (int userId : userIds) {
+                    changedRollback |= dataHelper.restoreAppData(
+                            info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo);
+                }
+                // We've updated metadata about this rollback, so save it to flash.
+                if (changedRollback) {
+                    RollbackStore.saveRollback(this);
+                }
+                break;
+            }
+        }
+        return foundPackage;
+    }
+
+    /**
+     * Deletes app data snapshots associated with this rollback, and moves to the DELETED state.
+     */
+    @GuardedBy("getLock")
+    void delete(AppDataRollbackHelper dataHelper) {
+        for (PackageRollbackInfo pkgInfo : info.getPackages()) {
+            IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers();
+            for (int i = 0; i < snapshottedUsers.size(); i++) {
+                // Destroy app data snapshot.
+                int userId = snapshottedUsers.get(i);
+
+                dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId);
+            }
+        }
+
+        RollbackStore.deleteRollback(this);
+        mState = ROLLBACK_STATE_DELETED;
     }
 
     /**
@@ -288,8 +582,8 @@
      */
     @GuardedBy("getLock")
     boolean includesPackage(String packageName) {
-        for (PackageRollbackInfo info : info.getPackages()) {
-            if (info.getPackageName().equals(packageName)) {
+        for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) {
+            if (packageRollbackInfo.getPackageName().equals(packageName)) {
                 return true;
             }
         }
@@ -302,9 +596,10 @@
      */
     @GuardedBy("getLock")
     boolean includesPackageWithDifferentVersion(String packageName, long versionCode) {
-        for (PackageRollbackInfo info : info.getPackages()) {
-            if (info.getPackageName().equals(packageName)
-                    && info.getVersionRolledBackFrom().getLongVersionCode() != versionCode) {
+        for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+            if (pkgRollbackInfo.getPackageName().equals(packageName)
+                    && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()
+                    != versionCode) {
                 return true;
             }
         }
@@ -317,8 +612,8 @@
     @GuardedBy("getLock")
     List<String> getPackageNames() {
         List<String> result = new ArrayList<>();
-        for (PackageRollbackInfo info : info.getPackages()) {
-            result.add(info.getPackageName());
+        for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+            result.add(pkgRollbackInfo.getPackageName());
         }
         return result;
     }
@@ -329,9 +624,9 @@
     @GuardedBy("getLock")
     List<String> getApexPackageNames() {
         List<String> result = new ArrayList<>();
-        for (PackageRollbackInfo info : info.getPackages()) {
-            if (info.isApex()) {
-                result.add(info.getPackageName());
+        for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+            if (pkgRollbackInfo.isApex()) {
+                result.add(pkgRollbackInfo.getPackageName());
             }
         }
         return result;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e8e448a..2221dff 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,7 +43,6 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -53,10 +52,8 @@
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
-import android.util.SparseLongArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
@@ -64,7 +61,6 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.security.SecureRandom;
 import java.time.Instant;
@@ -339,7 +335,7 @@
                         synchronized (rollback.getLock()) {
                             rollback.setTimestamp(
                                     rollback.getTimestamp().plusMillis(timeDifference));
-                            saveRollback(rollback);
+                            RollbackStore.saveRollback(rollback);
                         }
                     }
                 }
@@ -366,154 +362,13 @@
 
         Rollback rollback = getRollbackForId(rollbackId);
         if (rollback == null) {
-            sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+            sendFailure(
+                    mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
                     "Rollback unavailable");
             return;
         }
         synchronized (rollback.getLock()) {
-            if (!rollback.isAvailable()) {
-                sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
-                        "Rollback unavailable");
-                return;
-            }
-
-            // Get a context for the caller to use to install the downgraded
-            // version of the package.
-            final Context context;
-            try {
-                context = mContext.createPackageContext(callerPackageName, 0);
-            } catch (PackageManager.NameNotFoundException e) {
-                sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
-                        "Invalid callerPackageName");
-                return;
-            }
-
-            PackageManager pm = context.getPackageManager();
-            try {
-                PackageInstaller packageInstaller = pm.getPackageInstaller();
-                PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
-                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-                parentParams.setRequestDowngrade(true);
-                parentParams.setMultiPackage();
-                if (rollback.isStaged()) {
-                    parentParams.setStaged();
-                }
-
-                int parentSessionId = packageInstaller.createSession(parentParams);
-                PackageInstaller.Session parentSession = packageInstaller.openSession(
-                        parentSessionId);
-
-                for (PackageRollbackInfo info : rollback.info.getPackages()) {
-                    PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
-                            PackageInstaller.SessionParams.MODE_FULL_INSTALL);
-                    // TODO: We can't get the installerPackageName for apex
-                    // (b/123920130). Is it okay to ignore the installer package
-                    // for apex?
-                    if (!info.isApex()) {
-                        String installerPackageName =
-                                pm.getInstallerPackageName(info.getPackageName());
-                        if (installerPackageName != null) {
-                            params.setInstallerPackageName(installerPackageName);
-                        }
-                    }
-                    params.setRequestDowngrade(true);
-                    params.setRequiredInstalledVersionCode(
-                            info.getVersionRolledBackFrom().getLongVersionCode());
-                    if (rollback.isStaged()) {
-                        params.setStaged();
-                    }
-                    if (info.isApex()) {
-                        params.setInstallAsApex();
-                    }
-                    int sessionId = packageInstaller.createSession(params);
-                    PackageInstaller.Session session = packageInstaller.openSession(sessionId);
-                    File[] packageCodePaths = RollbackStore.getPackageCodePaths(
-                            rollback, info.getPackageName());
-                    if (packageCodePaths == null) {
-                        sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
-                                "Backup copy of package inaccessible");
-                        return;
-                    }
-
-                    for (File packageCodePath : packageCodePaths) {
-                        try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
-                                ParcelFileDescriptor.MODE_READ_ONLY)) {
-                            final long token = Binder.clearCallingIdentity();
-                            try {
-                                session.write(packageCodePath.getName(), 0,
-                                        packageCodePath.length(),
-                                        fd);
-                            } finally {
-                                Binder.restoreCallingIdentity(token);
-                            }
-                        }
-                    }
-                    parentSession.addChildSessionId(sessionId);
-                }
-
-                final LocalIntentReceiver receiver = new LocalIntentReceiver(
-                        (Intent result) -> getHandler().post(() -> {
-
-                            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                                    PackageInstaller.STATUS_FAILURE);
-                            if (status != PackageInstaller.STATUS_SUCCESS) {
-                                // Committing the rollback failed, but we
-                                // still have all the info we need to try
-                                // rolling back again, so restore the rollback
-                                // state to how it was before we tried
-                                // committing.
-                                // TODO: Should we just kill this rollback if
-                                // commit failed? Why would we expect commit
-                                // not to fail again?
-                                // TODO: Could this cause a rollback to be
-                                // resurrected if it should otherwise have
-                                // expired by now?
-                                synchronized (rollback.getLock()) {
-                                    rollback.setAvailable();
-                                    rollback.setRestoreUserDataInProgress(false);
-                                }
-                                sendFailure(statusReceiver,
-                                        RollbackManager.STATUS_FAILURE_INSTALL,
-                                        "Rollback downgrade install failed: "
-                                                + result.getStringExtra(
-                                                PackageInstaller.EXTRA_STATUS_MESSAGE));
-                                return;
-                            }
-
-                            synchronized (rollback.getLock()) {
-                                if (!rollback.isStaged()) {
-                                    // All calls to restoreUserData should have
-                                    // completed by now for a non-staged install.
-                                    rollback.setRestoreUserDataInProgress(false);
-                                }
-
-                                rollback.info.setCommittedSessionId(parentSessionId);
-                                rollback.info.getCausePackages().addAll(causePackages);
-                                RollbackStore.deletePackageCodePaths(rollback);
-                                saveRollback(rollback);
-                            }
-
-                            sendSuccess(statusReceiver);
-
-                            Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
-
-                            for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
-                                mContext.sendBroadcastAsUser(broadcast,
-                                        userInfo.getUserHandle(),
-                                        Manifest.permission.MANAGE_ROLLBACKS);
-                            }
-                        })
-                );
-
-                rollback.setCommitted();
-                rollback.setRestoreUserDataInProgress(true);
-                parentSession.commit(receiver.getIntentSender());
-            } catch (IOException e) {
-                Slog.e(TAG, "Rollback failed", e);
-                sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
-                        "IOException: " + e.toString());
-                return;
-            }
+            rollback.commit(mContext, causePackages, callerPackageName, statusReceiver);
         }
     }
 
@@ -552,7 +407,7 @@
                 synchronized (rollback.getLock()) {
                     if (rollback.includesPackage(packageName)) {
                         iter.remove();
-                        deleteRollback(rollback);
+                        rollback.delete(mAppDataRollbackHelper);
                     }
                 }
             }
@@ -596,7 +451,7 @@
                 synchronized (rollback.getLock()) {
                     if (mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(
                             userId, rollback)) {
-                        saveRollback(rollback);
+                        RollbackStore.saveRollback(rollback);
                     }
                 }
             }
@@ -658,7 +513,7 @@
                         // mRollbacks, or is it okay to leave as
                         // unavailable until the next reboot when it will go
                         // away on its own?
-                        deleteRollback(rollback);
+                        rollback.delete(mAppDataRollbackHelper);
                     } else if (session.isStagedSessionApplied()) {
                         makeRollbackAvailable(rollback);
                     }
@@ -674,7 +529,7 @@
                     if (session != null) {
                         if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
                             rollback.setRestoreUserDataInProgress(false);
-                            saveRollback(rollback);
+                            RollbackStore.saveRollback(rollback);
                         }
                     }
                 }
@@ -716,7 +571,7 @@
                             && rollback.includesPackageWithDifferentVersion(packageName,
                             installedVersion)) {
                         iter.remove();
-                        deleteRollback(rollback);
+                        rollback.delete(mAppDataRollbackHelper);
                     }
                 }
             }
@@ -738,27 +593,14 @@
      * @param status the RollbackManager.STATUS_* code with the failure.
      * @param message the failure message.
      */
-    private void sendFailure(IntentSender statusReceiver, @RollbackManager.Status int status,
-            String message) {
+    static void sendFailure(Context context, IntentSender statusReceiver,
+            @RollbackManager.Status int status, String message) {
         Slog.e(TAG, message);
         try {
             final Intent fillIn = new Intent();
             fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
             fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
-            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
-        } catch (IntentSender.SendIntentException e) {
-            // Nowhere to send the result back to, so don't bother.
-        }
-    }
-
-    /**
-     * Notifies an IntentSender of success.
-     */
-    private void sendSuccess(IntentSender statusReceiver) {
-        try {
-            final Intent fillIn = new Intent();
-            fillIn.putExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS);
-            statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+            statusReceiver.sendIntent(context, 0, fillIn, null, null);
         } catch (IntentSender.SendIntentException e) {
             // Nowhere to send the result back to, so don't bother.
         }
@@ -781,7 +623,7 @@
                             rollback.getTimestamp()
                                     .plusMillis(mRollbackLifetimeDurationInMillis))) {
                         iter.remove();
-                        deleteRollback(rollback);
+                        rollback.delete(mAppDataRollbackHelper);
                     } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
                         oldest = rollback.getTimestamp();
                     }
@@ -954,7 +796,7 @@
         }
 
         // Get information about the package to be installed.
-        PackageParser.PackageLite newPackage = null;
+        PackageParser.PackageLite newPackage;
         try {
             newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
         } catch (PackageParser.PackageParserException e) {
@@ -973,11 +815,9 @@
             return false;
         }
 
-        VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
         final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
 
         // Get information about the currently installed package.
-        PackageManager pm = mContext.getPackageManager();
         final PackageInfo pkgInfo;
         try {
             pkgInfo = getPackageInfo(packageName);
@@ -988,32 +828,12 @@
             return false;
         }
 
-        VersionedPackage installedVersion = new VersionedPackage(packageName,
-                pkgInfo.getLongVersionCode());
-
-        PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
-                newVersion, installedVersion,
-                new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
-                isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
-
-
-        try {
-            ApplicationInfo appInfo = pkgInfo.applicationInfo;
-            RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
-            if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
-                for (String sourceDir : appInfo.splitSourceDirs) {
-                    RollbackStore.backupPackageCodePath(rollback, packageName, sourceDir);
-                }
-            }
-        } catch (IOException e) {
-            Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
-            return false;
-        }
-
+        ApplicationInfo appInfo = pkgInfo.applicationInfo;
         synchronized (rollback.getLock()) {
-            rollback.info.getPackages().add(packageRollbackInfo);
+            return rollback.enableForPackage(packageName, newPackage.versionCode,
+                    pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
+                    appInfo.splitSourceDirs);
         }
-        return true;
     }
 
     @Override
@@ -1026,7 +846,7 @@
 
         getHandler().post(() -> {
             snapshotUserDataInternal(packageName, userIds);
-            restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
+            restoreUserDataInternal(packageName, userIds, appId, seInfo);
             final PackageManagerInternal pmi = LocalServices.getService(
                     PackageManagerInternal.class);
             pmi.finishPackageInstall(token, false);
@@ -1039,69 +859,32 @@
             for (int i = 0; i < mRollbacks.size(); i++) {
                 Rollback rollback = mRollbacks.get(i);
                 synchronized (rollback.getLock()) {
-                    if (!rollback.isEnabling()) {
-                        continue;
-                    }
-
-                    for (PackageRollbackInfo info : rollback.info.getPackages()) {
-                        if (info.getPackageName().equals(packageName)) {
-                            mAppDataRollbackHelper.snapshotAppData(
-                                    rollback.info.getRollbackId(), info, userIds);
-                            saveRollback(rollback);
-                            break;
-                        }
-                    }
+                    rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
                 }
             }
             // non-staged installs
-            PackageRollbackInfo info;
             for (NewRollback rollback : mNewRollbacks) {
                 synchronized (rollback.rollback.getLock()) {
-                    info = getPackageRollbackInfo(rollback.rollback, packageName);
-                    if (info != null) {
-                        mAppDataRollbackHelper.snapshotAppData(
-                                rollback.rollback.info.getRollbackId(), info, userIds);
-                        saveRollback(rollback.rollback);
-                    }
+                    rollback.rollback.snapshotUserData(
+                            packageName, userIds, mAppDataRollbackHelper);
                 }
             }
         }
     }
 
-    private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
-            long ceDataInode, String seInfo, int token) {
-        PackageRollbackInfo info = null;
-        Rollback rollback = null;
+    private void restoreUserDataInternal(
+            String packageName, int[] userIds, int appId, String seInfo) {
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                Rollback candidate = mRollbacks.get(i);
-                synchronized (candidate.getLock()) {
-                    if (candidate.isRestoreUserDataInProgress()) {
-                        info = getPackageRollbackInfo(candidate, packageName);
-                        if (info != null) {
-                            rollback = candidate;
-                            break;
-                        }
+                Rollback rollback = mRollbacks.get(i);
+                synchronized (rollback.getLock()) {
+                    if (rollback.restoreUserDataForPackageIfInProgress(
+                            packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) {
+                        return;
                     }
                 }
             }
         }
-
-        if (rollback == null) {
-            return;
-        }
-
-        for (int userId : userIds) {
-            synchronized (rollback.getLock()) {
-                final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
-                        rollback.info.getRollbackId(), info, userId, appId, seInfo);
-
-                // We've updated metadata about this rollback, so save it to flash.
-                if (changedRollback) {
-                    saveRollback(rollback);
-                }
-            }
-        }
     }
 
     @Override
@@ -1189,7 +972,7 @@
             if (rollback != null) {
                 synchronized (rollback.getLock()) {
                     rollback.setApkSessionId(apkSessionId);
-                    saveRollback(rollback);
+                    RollbackStore.saveRollback(rollback);
                 }
             }
         });
@@ -1238,8 +1021,7 @@
      * Returns -1 if the package is not currently installed.
      */
     private long getInstalledPackageVersion(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        PackageInfo pkgInfo = null;
+        PackageInfo pkgInfo;
         try {
             pkgInfo = getPackageInfo(packageName);
         } catch (PackageManager.NameNotFoundException e) {
@@ -1250,9 +1032,9 @@
     }
 
     /**
-     * Gets PackageInfo for the given package.
-     * Matches any user and apex. Returns null if no such package is
-     * installed.
+     * Gets PackageInfo for the given package. Matches any user and apex.
+     *
+     * @throws PackageManager.NameNotFoundException if no such package is installed.
      */
     private PackageInfo getPackageInfo(String packageName)
             throws PackageManager.NameNotFoundException {
@@ -1268,13 +1050,6 @@
         }
     }
 
-
-    private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
-        return a != null && b != null
-            && a.getPackageName().equals(b.getPackageName())
-            && a.getLongVersionCode() == b.getLongVersionCode();
-    }
-
     private class SessionCallback extends PackageInstaller.SessionCallback {
 
         @Override
@@ -1326,23 +1101,23 @@
         synchronized (rollback.getLock()) {
             if (!success) {
                 // The install session was aborted, clean up the pending install.
-                deleteRollback(rollback);
+                rollback.delete(mAppDataRollbackHelper);
                 return null;
             }
             if (newRollback.isCancelled) {
                 Slog.e(TAG, "Rollback has been cancelled by PackageManager");
-                deleteRollback(rollback);
+                rollback.delete(mAppDataRollbackHelper);
                 return null;
             }
 
 
             if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
                 Slog.e(TAG, "Failed to enable rollback for all packages in session.");
-                deleteRollback(rollback);
+                rollback.delete(mAppDataRollbackHelper);
                 return null;
             }
 
-            saveRollback(rollback);
+            RollbackStore.saveRollback(rollback);
         }
         synchronized (mLock) {
             // Note: There is a small window of time between when
@@ -1363,17 +1138,12 @@
 
     @GuardedBy("rollback.getLock")
     private void makeRollbackAvailable(Rollback rollback) {
-        // TODO: What if the rollback has since been expired, for example due
-        // to a new package being installed. Won't this revive an expired
-        // rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
-        rollback.setAvailable();
-        rollback.setTimestamp(Instant.now());
-        saveRollback(rollback);
+        rollback.makeAvailable();
 
         // TODO(zezeozue): Provide API to explicitly start observing instead
         // of doing this for all rollbacks. If we do this for all rollbacks,
         // should document in PackageInstaller.SessionParams#setEnableRollback
-        // After enabling and commiting any rollback, observe packages and
+        // After enabling and committing any rollback, observe packages and
         // prepare to rollback if packages crashes too frequently.
         mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
                 mRollbackLifetimeDurationInMillis);
@@ -1396,22 +1166,6 @@
         return null;
     }
 
-    /**
-     * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
-     * a specified {@code Rollback}.
-     */
-    @GuardedBy("rollback.getLock")
-    private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
-            String packageName) {
-        for (PackageRollbackInfo info : rollback.info.getPackages()) {
-            if (info.getPackageName().equals(packageName)) {
-                return info;
-            }
-        }
-
-        return null;
-    }
-
     @GuardedBy("mLock")
     private int allocateRollbackIdLocked() {
         int n = 0;
@@ -1427,35 +1181,6 @@
         throw new IllegalStateException("Failed to allocate rollback ID");
     }
 
-    @GuardedBy("rollback.getLock")
-    private void deleteRollback(Rollback rollback) {
-        for (PackageRollbackInfo info : rollback.info.getPackages()) {
-            IntArray snapshottedUsers = info.getSnapshottedUsers();
-            for (int i = 0; i < snapshottedUsers.size(); i++) {
-                int userId = snapshottedUsers.get(i);
-                mAppDataRollbackHelper.destroyAppDataSnapshot(rollback.info.getRollbackId(),
-                        info, userId);
-            }
-        }
-        mRollbackStore.deleteRollback(rollback);
-    }
-
-    /**
-     * Saves a rollback, swallowing any IOExceptions.
-     * For those times when it's not obvious what to do about the IOException.
-     * TODO: Double check we can't do a better job handling the IOException in
-     * a cases where this method is called.
-     */
-    @GuardedBy("rollback.getLock")
-    private void saveRollback(Rollback rollback) {
-        try {
-            mRollbackStore.saveRollback(rollback);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Unable to save rollback for: "
-                    + rollback.info.getRollbackId(), ioe);
-        }
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index b6d1f187..a9331aa 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -253,7 +253,7 @@
      * Saves the given rollback to persistent storage.
      */
     @GuardedBy("rollback.getLock")
-    void saveRollback(Rollback rollback) throws IOException {
+    static void saveRollback(Rollback rollback) {
         try {
             JSONObject dataJson = new JSONObject();
             dataJson.put("info", rollbackInfoToJson(rollback.info));
@@ -266,15 +266,15 @@
             PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
             pw.println(dataJson.toString());
             pw.close();
-        } catch (JSONException e) {
-            throw new IOException(e);
+        } catch (JSONException | IOException e) {
+            Slog.e(TAG, "Unable to save rollback for: " + rollback.info.getRollbackId(), e);
         }
     }
 
     /**
      * Removes all persistent storage associated with the given rollback.
      */
-    void deleteRollback(Rollback rollback) {
+    static void deleteRollback(Rollback rollback) {
         removeFile(rollback.getBackupDir());
     }
 
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
index 2b25b89..8431ae4 100644
--- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
@@ -72,6 +72,30 @@
         return null;
     }
 
+    /**
+     * Reads cmdline of a process from procfs.
+     *
+     * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+     * if the file is not available.
+     */
+    public static String readCmdlineFromProcfs(int pid) {
+        return parseCmdline(readFile("/proc/" + pid + "/cmdline"));
+    }
+
+    /**
+     * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
+     *
+     * Parsing is required to strip anything after the first null byte.
+     */
+    @VisibleForTesting
+    static String parseCmdline(String contents) {
+        int firstNullByte = contents.indexOf("\0");
+        if (firstNullByte == -1) {
+            return contents;
+        }
+        return contents.substring(0, firstNullByte);
+    }
+
     private static String readFile(String path) {
         try {
             final File file = new File(path);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index dbf06a5..54bb5f7 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1743,7 +1743,12 @@
             return START_SUCCESS;
         }
 
-        if (!mMovedToFront && mDoResume) {
+        if (mMovedToFront) {
+            // We moved the task to front, use starting window to hide initial drawn delay.
+            targetTaskTop.showStartingWindow(null /* prev */, false /* newTask */,
+                    true /* taskSwitch */);
+        } else if (mDoResume) {
+            // Make sure the stack and its belonging display are moved to topmost.
             mTargetStack.moveToFront("intentActivityFound");
         }
         // We didn't do anything...  but it was needed (a.k.a., client don't use that intent!)
@@ -2349,11 +2354,6 @@
                 }
 
                 mOptions = null;
-
-                // We are moving a task to the front, use starting window to hide initial drawn
-                // delay.
-                intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
-                        true /* taskSwitch */);
             }
         }
         // Need to update mTargetStack because if task was moved out of it, the original stack may
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6b2f9da..59f086e 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -449,9 +449,9 @@
                 "Checking " + apps.size() + " opening apps (frozen="
                         + mService.mDisplayFrozen + " timeout="
                         + mDisplayContent.mAppTransition.isTimeout() + ")...");
-        final ScreenRotationAnimation screenRotationAnimation =
-                mService.mAnimator.getScreenRotationAnimationLocked(
-                        Display.DEFAULT_DISPLAY);
+
+        final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
+                Display.DEFAULT_DISPLAY).getRotationAnimation();
 
         if (!mDisplayContent.mAppTransition.isTimeout()) {
             // Imagine the case where we are changing orientation due to an app transition, but a
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 7557271a..a25aab2 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.util.Slog;
 import android.view.Surface.OutOfResourcesException;
@@ -33,14 +32,15 @@
  * Four black surfaces put together to make a black frame.
  */
 public class BlackFrame {
-    class BlackSurface {
+    static class BlackSurface {
         final int left;
         final int top;
         final int layer;
         final SurfaceControl surface;
 
         BlackSurface(SurfaceControl.Transaction transaction, int layer,
-                int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
+                int l, int t, int r, int b, DisplayContent dc,
+                SurfaceControl surfaceControl) throws OutOfResourcesException {
             left = l;
             top = t;
             this.layer = layer;
@@ -50,7 +50,7 @@
             surface = dc.makeOverlay()
                     .setName("BlackSurface")
                     .setColorLayer()
-                    .setParent(null) // TODO: Work-around for b/69259549
+                    .setParent(surfaceControl)
                     .build();
             transaction.setWindowCrop(surface, w, h);
             transaction.setLayerStack(surface, dc.getDisplayId());
@@ -61,43 +61,12 @@
             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
                             "  BLACK " + surface + ": CREATE layer=" + layer);
         }
-
-        void setAlpha(SurfaceControl.Transaction t, float alpha) {
-            t.setAlpha(surface, alpha);
-        }
-
-        void setMatrix(SurfaceControl.Transaction t, Matrix matrix) {
-            mTmpMatrix.setTranslate(left, top);
-            mTmpMatrix.postConcat(matrix);
-            mTmpMatrix.getValues(mTmpFloats);
-            t.setPosition(surface, mTmpFloats[Matrix.MTRANS_X],
-                    mTmpFloats[Matrix.MTRANS_Y]);
-            t.setMatrix(surface,
-                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
-                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
-            if (false) {
-                Slog.i(TAG_WM, "Black Surface @ (" + left + "," + top + "): ("
-                        + mTmpFloats[Matrix.MTRANS_X] + ","
-                        + mTmpFloats[Matrix.MTRANS_Y] + ") matrix=["
-                        + mTmpFloats[Matrix.MSCALE_X] + ","
-                        + mTmpFloats[Matrix.MSCALE_Y] + "]["
-                        + mTmpFloats[Matrix.MSKEW_X] + ","
-                        + mTmpFloats[Matrix.MSKEW_Y] + "]");
-            }
-        }
-
-        void clearMatrix(SurfaceControl.Transaction t) {
-            t.setMatrix(surface, 1, 0, 0, 1);
-        }
     }
 
-    final Rect mOuterRect;
-    final Rect mInnerRect;
-    final Matrix mTmpMatrix = new Matrix();
-    final float[] mTmpFloats = new float[9];
-    final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
+    private final Rect mOuterRect;
+    private final Rect mInnerRect;
+    private final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
 
-    final boolean mForceDefaultOrientation;
     private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
 
     public void printTo(String prefix, PrintWriter pw) {
@@ -114,12 +83,12 @@
     }
 
     public BlackFrame(Supplier<SurfaceControl.Transaction> factory, SurfaceControl.Transaction t,
-            Rect outer, Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation)
+            Rect outer, Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation,
+            SurfaceControl surfaceControl)
             throws OutOfResourcesException {
         boolean success = false;
 
         mTransactionFactory = factory;
-        mForceDefaultOrientation = forceDefaultOrientation;
 
         // TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
         // b/68253229
@@ -128,19 +97,20 @@
         try {
             if (outer.top < inner.top) {
                 mBlackSurfaces[0] = new BlackSurface(t, layer,
-                        outer.left, outer.top, inner.right, inner.top, dc);
+                        outer.left, outer.top, inner.right, inner.top, dc, surfaceControl);
             }
             if (outer.left < inner.left) {
                 mBlackSurfaces[1] = new BlackSurface(t, layer,
-                        outer.left, inner.top, inner.left, outer.bottom, dc);
+                        outer.left, inner.top, inner.left, outer.bottom, dc, surfaceControl);
             }
             if (outer.bottom > inner.bottom) {
                 mBlackSurfaces[2] = new BlackSurface(t, layer,
-                        inner.left, inner.bottom, outer.right, outer.bottom, dc);
+                        inner.left, inner.bottom, outer.right, outer.bottom, dc,
+                        surfaceControl);
             }
             if (outer.right > inner.right) {
                 mBlackSurfaces[3] = new BlackSurface(t, layer,
-                        inner.right, outer.top, outer.right, inner.bottom, dc);
+                        inner.right, outer.top, outer.right, inner.bottom, dc, surfaceControl);
             }
             success = true;
         } finally {
@@ -151,51 +121,17 @@
     }
 
     public void kill() {
-        if (mBlackSurfaces != null) {
-            SurfaceControl.Transaction t = mTransactionFactory.get();
-            for (int i=0; i<mBlackSurfaces.length; i++) {
-                if (mBlackSurfaces[i] != null) {
-                    if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
+        SurfaceControl.Transaction t = mTransactionFactory.get();
+        for (int i = 0; i < mBlackSurfaces.length; i++) {
+            if (mBlackSurfaces[i] != null) {
+                if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+                    Slog.i(TAG_WM,
                             "  BLACK " + mBlackSurfaces[i].surface + ": DESTROY");
-                    t.remove(mBlackSurfaces[i].surface);
-                    mBlackSurfaces[i] = null;
                 }
-            }
-            t.apply();
-        }
-    }
-
-    public void hide(SurfaceControl.Transaction t) {
-        if (mBlackSurfaces != null) {
-            for (int i=0; i<mBlackSurfaces.length; i++) {
-                if (mBlackSurfaces[i] != null) {
-                    t.hide(mBlackSurfaces[i].surface);
-                }
+                t.remove(mBlackSurfaces[i].surface);
+                mBlackSurfaces[i] = null;
             }
         }
-    }
-
-    public void setAlpha(SurfaceControl.Transaction t, float alpha) {
-        for (int i=0; i<mBlackSurfaces.length; i++) {
-            if (mBlackSurfaces[i] != null) {
-                mBlackSurfaces[i].setAlpha(t, alpha);
-            }
-        }
-    }
-
-    public void setMatrix(SurfaceControl.Transaction t, Matrix matrix) {
-        for (int i=0; i<mBlackSurfaces.length; i++) {
-            if (mBlackSurfaces[i] != null) {
-                mBlackSurfaces[i].setMatrix(t, matrix);
-            }
-        }
-    }
-
-    public void clearMatrix(SurfaceControl.Transaction t) {
-        for (int i=0; i<mBlackSurfaces.length; i++) {
-            if (mBlackSurfaces[i] != null) {
-                mBlackSurfaces[i].clearMatrix(t);
-            }
-        }
+        t.apply();
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6462744..755180e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -497,6 +497,8 @@
     /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
     final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
 
+    private ScreenRotationAnimation mScreenRotationAnimation;
+
     /**
      * We organize all top-level Surfaces in to the following layers.
      * mOverlayLayer contains a few Surfaces which are always on top of others
@@ -521,9 +523,6 @@
      */
     private int mDeferUpdateImeTargetCount;
 
-    /** Temporary float array to retrieve 3x3 matrix values. */
-    private final float[] mTmpFloats = new float[9];
-
     private MagnificationSpec mMagnificationSpec;
 
     private InputMonitor mInputMonitor;
@@ -1309,9 +1308,9 @@
     void applyRotationLocked(final int oldRotation, final int rotation) {
         mDisplayRotation.applyCurrentRotation(rotation);
         final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
-        final ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
-                ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
         final Transaction transaction = getPendingTransaction();
+        ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
+                ? null : getRotationAnimation();
         // We need to update our screen size information to match the new rotation. If the rotation
         // has actually changed then this method will return true and, according to the comment at
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -2393,6 +2392,7 @@
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             mPointerEventDispatcher.dispose();
+            setRotationAnimation(null);
             mWmService.mAnimator.removeDisplayLocked(mDisplayId);
             mWindowingLayer.release();
             mOverlayLayer.release();
@@ -2557,6 +2557,17 @@
         return delta;
     }
 
+    public void setRotationAnimation(ScreenRotationAnimation screenRotationAnimation) {
+        if (mScreenRotationAnimation != null) {
+            mScreenRotationAnimation.kill();
+        }
+        mScreenRotationAnimation = screenRotationAnimation;
+    }
+
+    public ScreenRotationAnimation getRotationAnimation() {
+        return mScreenRotationAnimation;
+    }
+
     private static void createRotationMatrix(int rotation, float displayWidth, float displayHeight,
             Matrix outMatrix) {
         // For rotations without Z-ordering we don't need the target rectangle's position.
@@ -2619,8 +2630,7 @@
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
         proto.write(ROTATION, getRotation());
-        final ScreenRotationAnimation screenRotationAnimation =
-                mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+        final ScreenRotationAnimation screenRotationAnimation = getRotationAnimation();
         if (screenRotationAnimation != null) {
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
@@ -2732,6 +2742,16 @@
             }
         }
 
+        final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
+        if (rotationAnimation != null) {
+            pw.print(subPrefix);
+            pw.println("  mScreenRotationAnimation:");
+            rotationAnimation.printTo("  ", pw);
+        } else if (dumpAll) {
+            pw.print(subPrefix);
+            pw.println("  no ScreenRotationAnimation ");
+        }
+
         pw.println();
 
         // Dump stack references
@@ -3741,7 +3761,7 @@
         convertCropForSurfaceFlinger(frame, rot, dw, dh);
 
         final ScreenRotationAnimation screenRotationAnimation =
-                mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+                mWmService.mRoot.getDisplayContent(DEFAULT_DISPLAY).getRotationAnimation();
         final boolean inRotation = screenRotationAnimation != null &&
                 screenRotationAnimation.isAnimating();
         if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
@@ -4666,20 +4686,7 @@
     void prepareSurfaces() {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
         try {
-            final ScreenRotationAnimation screenRotationAnimation =
-                    mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
             final Transaction transaction = getPendingTransaction();
-            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
-                screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
-                transaction.setMatrix(mWindowingLayer,
-                        mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
-                        mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
-                transaction.setPosition(mWindowingLayer,
-                        mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
-                transaction.setAlpha(mWindowingLayer,
-                        screenRotationAnimation.getEnterTransformation().getAlpha());
-            }
-
             super.prepareSurfaces();
 
             // TODO: Once we totally eliminate global transaction we will pass transaction in here
@@ -4909,6 +4916,10 @@
         return mWindowingLayer;
     }
 
+    SurfaceControl getOverlayLayer() {
+        return mOverlayLayer;
+    }
+
     /**
      * Updates the display's system gesture exclusion.
      *
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 4d188f4..f08b4fc 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -404,7 +404,7 @@
             }
 
             final ScreenRotationAnimation screenRotationAnimation =
-                    mService.mAnimator.getScreenRotationAnimationLocked(displayId);
+                    mDisplayContent.getRotationAnimation();
             if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                 // Rotation updates cannot be performed while the previous rotation change animation
                 // is still in progress. Skip this update. We will try updating again after the
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index f8f6334..d5f403f 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -113,7 +113,7 @@
 
                     final WindowState callingWin = mService.windowForClientLocked(
                             null, window, false);
-                    if (callingWin == null) {
+                    if (callingWin == null || callingWin.cantReceiveTouchInput()) {
                         Slog.w(TAG_WM, "Bad requesting window " + window);
                         return null;  // !!! TODO: throw here?
                     }
@@ -167,8 +167,7 @@
                     final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
                     if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
 
-                    final SurfaceControl.Transaction transaction =
-                            callingWin.getPendingTransaction();
+                    final SurfaceControl.Transaction transaction = mDragState.mTransaction;
                     transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
                     transaction.setPosition(
                             surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 17daabf..3c61694 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -120,7 +120,7 @@
     // A surface used to catch input events for the drag-and-drop operation.
     SurfaceControl mInputSurface;
 
-    private final SurfaceControl.Transaction mTransaction;
+    final SurfaceControl.Transaction mTransaction;
 
     private final Rect mTmpClipRect = new Rect();
 
@@ -129,7 +129,6 @@
      * {@code true} when {@link #closeLocked()} is called.
      */
     private boolean mIsClosing;
-    IBinder mTransferTouchFromToken;
 
     DragState(WindowManagerService service, DragDropController controller, IBinder token,
             SurfaceControl surface, int flags, IBinder localWin) {
@@ -167,12 +166,11 @@
 
         mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
         mTransaction.setWindowCrop(mInputSurface, mTmpClipRect);
-        mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token);
-        mTransferTouchFromToken = null;
 
-        // syncInputWindows here to ensure the input channel isn't removed before the transfer.
+        // syncInputWindows here to ensure the input window info is sent before the
+        // transferTouchFocus is called.
         mTransaction.syncInputWindows();
-        mTransaction.apply();
+        mTransaction.apply(true);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index cbaf098..c4bfe4b 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -27,6 +27,7 @@
 
 import android.content.Context;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -41,6 +42,34 @@
 
 import java.io.PrintWriter;
 
+/**
+ * This class handles the rotation animation when the device is rotated.
+ *
+ * <p>
+ * The screen rotation animation is composed of 4 different part:
+ * <ul>
+ * <li> The screenshot: <p>
+ *     A screenshot of the whole screen prior the change of orientation is taken to hide the
+ *     element resizing below. The screenshot is then animated to rotate and cross-fade to
+ *     the new orientation with the content in the new orientation.
+ *
+ * <li> The windows on the display: <p>y
+ *      Once the device is rotated, the screen and its content are in the new orientation. The
+ *      animation first rotate the new content into the old orientation to then be able to
+ *      animate to the new orientation
+ *
+ * <li> The exiting Blackframe: <p>
+ *     Because the change of orientation might change the width and height of the content (i.e
+ *     when rotating from portrait to landscape) we "crop" the new content using black frames
+ *     around the screenshot so the new content does not go beyond the screenshot's bounds
+ *
+ * <li> The entering Blackframe: <p>
+ *     The enter Blackframe is similar to the exit Blackframe but is only used when a custom
+ *     rotation animation is used and matches the new content size instead of the screenshot.
+ * </ul>
+ *
+ * Each part has its own Surface which are then animated by {@link SurfaceAnimator}s.
+ */
 class ScreenRotationAnimation {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
 
@@ -66,6 +95,8 @@
     private final Matrix mSnapshotFinalMatrix = new Matrix();
     private final Matrix mExitFrameFinalMatrix = new Matrix();
     private final WindowManagerService mService;
+    private SurfaceControl mEnterBlackFrameLayer;
+    private SurfaceControl mRotationLayer;
     private SurfaceControl mSurfaceControl;
     private BlackFrame mEnteringBlackFrame;
     private int mWidth, mHeight;
@@ -81,15 +112,14 @@
     // rotations.
     private Animation mRotateExitAnimation;
     private Animation mRotateEnterAnimation;
+    private Animation mRotateAlphaAnimation;
     private boolean mStarted;
     private boolean mAnimRunning;
     private boolean mFinishAnimReady;
     private long mFinishAnimStartTime;
     private boolean mForceDefaultOrientation;
     private BlackFrame mExitingBlackFrame;
-    private boolean mMoreRotateEnter;
-    private boolean mMoreRotateExit;
-    private long mHalfwayPoint;
+    private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
             boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
@@ -126,11 +156,23 @@
         mOriginalRotation = originalRotation;
         mOriginalWidth = originalWidth;
         mOriginalHeight = originalHeight;
+        mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
 
         final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
         try {
-            mSurfaceControl = displayContent.makeOverlay()
+            mRotationLayer = displayContent.makeOverlay()
+                    .setName("RotationLayer")
+                    .setContainerLayer()
+                    .build();
+
+            mEnterBlackFrameLayer = displayContent.makeOverlay()
+                    .setName("EnterBlackFrameLayer")
+                    .setContainerLayer()
+                    .build();
+
+            mSurfaceControl = displayContent.makeSurface(null)
                     .setName("ScreenshotSurface")
+                    .setParent(mRotationLayer)
                     .setBufferSize(mWidth, mHeight)
                     .setSecure(isSecure)
                     .build();
@@ -160,8 +202,10 @@
                 if (gb.containsSecureLayers()) {
                     t.setSecure(mSurfaceControl, true);
                 }
+                t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
                 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
                 t.setAlpha(mSurfaceControl, 0);
+                t.show(mRotationLayer);
                 t.show(mSurfaceControl);
             } else {
                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
@@ -211,22 +255,26 @@
         return mSurfaceControl != null;
     }
 
-    private void setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha) {
-        if (mSurfaceControl != null) {
-            matrix.getValues(mTmpFloats);
-            float x = mTmpFloats[Matrix.MTRANS_X];
-            float y = mTmpFloats[Matrix.MTRANS_Y];
-            if (mForceDefaultOrientation) {
-                mDisplayContent.getBounds(mCurrentDisplayRect);
-                x -= mCurrentDisplayRect.left;
-                y -= mCurrentDisplayRect.top;
-            }
-            t.setPosition(mSurfaceControl, x, y);
-            t.setMatrix(mSurfaceControl,
-                    mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
-                    mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
-            t.setAlpha(mSurfaceControl, alpha);
+    private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
+        if (mRotationLayer == null) {
+            return;
         }
+        matrix.getValues(mTmpFloats);
+        float x = mTmpFloats[Matrix.MTRANS_X];
+        float y = mTmpFloats[Matrix.MTRANS_Y];
+        if (mForceDefaultOrientation) {
+            mDisplayContent.getBounds(mCurrentDisplayRect);
+            x -= mCurrentDisplayRect.left;
+            y -= mCurrentDisplayRect.top;
+        }
+        t.setPosition(mRotationLayer, x, y);
+        t.setMatrix(mRotationLayer,
+                mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+                mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+
+        t.setAlpha(mSurfaceControl, (float) 1.0);
+        t.setAlpha(mRotationLayer, (float) 1.0);
+        t.show(mRotationLayer);
     }
 
     public void printTo(String prefix, PrintWriter pw) {
@@ -237,7 +285,9 @@
         if (mExitingBlackFrame != null) {
             mExitingBlackFrame.printTo(prefix + "  ", pw);
         }
-        pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame);
+        pw.print(prefix);
+        pw.print("mEnteringBlackFrame=");
+        pw.println(mEnteringBlackFrame);
         if (mEnteringBlackFrame != null) {
             mEnteringBlackFrame.printTo(prefix + "  ", pw);
         }
@@ -283,7 +333,7 @@
         int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
         createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
 
-        setSnapshotTransform(t, mSnapshotInitialMatrix, 1.0f);
+        setRotationTransform(t, mSnapshotInitialMatrix);
     }
 
     /**
@@ -304,6 +354,9 @@
         // Figure out how the screen has moved from the original rotation.
         int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
 
+        mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+                com.android.internal.R.anim.screen_rotate_alpha);
+
         final boolean customAnim;
         if (exitAnim != 0 && enterAnim != 0) {
             customAnim = true;
@@ -353,6 +406,8 @@
         mRotateExitAnimation.scaleCurrentDuration(animationScale);
         mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
         mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+        mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
+        mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
 
         if (!customAnim && mExitingBlackFrame == null) {
             try {
@@ -373,13 +428,12 @@
                     outer = mCurrentDisplayRect;
                     inner = mOriginalDisplayRect;
                 } else {
-                    outer = new Rect(-mOriginalWidth * 1, -mOriginalHeight * 1,
-                            mOriginalWidth * 2, mOriginalHeight * 2);
-                    inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
+                    outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
+                    inner = new Rect(0, 0, mWidth, mHeight);
                 }
                 mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
-                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
-                mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix);
+                        SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
+                        mRotationLayer);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             }
@@ -387,16 +441,17 @@
 
         if (customAnim && mEnteringBlackFrame == null) {
             try {
-                Rect outer = new Rect(-finalWidth * 1, -finalHeight * 1,
+                Rect outer = new Rect(-finalWidth, -finalHeight,
                         finalWidth * 2, finalHeight * 2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
                 mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
-                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
+                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             }
         }
 
+        mSurfaceRotationAnimationController.startAnimation();
         return true;
     }
 
@@ -421,14 +476,31 @@
     }
 
     public void kill() {
+        if (mSurfaceRotationAnimationController != null) {
+            mSurfaceRotationAnimationController.cancel();
+            mSurfaceRotationAnimationController = null;
+        }
         if (mSurfaceControl != null) {
             if (SHOW_TRANSACTIONS ||
                     SHOW_SURFACE_ALLOC) {
                 Slog.i(TAG_WM,
                         "  FREEZE " + mSurfaceControl + ": DESTROY");
             }
-            mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
             mSurfaceControl = null;
+            SurfaceControl.Transaction t = mService.mTransactionFactory.get();
+            if (mRotationLayer != null) {
+                if (mRotationLayer.isValid()) {
+                    t.remove(mRotationLayer);
+                }
+                mRotationLayer = null;
+            }
+            if (mEnterBlackFrameLayer != null) {
+                if (mEnterBlackFrameLayer.isValid()) {
+                    t.remove(mEnterBlackFrameLayer);
+                }
+                mEnterBlackFrameLayer = null;
+            }
+            t.apply();
         }
         if (mExitingBlackFrame != null) {
             mExitingBlackFrame.kill();
@@ -446,124 +518,186 @@
             mRotateEnterAnimation.cancel();
             mRotateEnterAnimation = null;
         }
+        if (mRotateAlphaAnimation != null) {
+            mRotateAlphaAnimation.cancel();
+            mRotateAlphaAnimation = null;
+        }
     }
 
     public boolean isAnimating() {
-        return hasAnimations();
+        return mSurfaceRotationAnimationController != null
+                && mSurfaceRotationAnimationController.isAnimating();
     }
 
     public boolean isRotating() {
         return mCurRotation != mOriginalRotation;
     }
 
-    private boolean hasAnimations() {
-        return mRotateEnterAnimation != null || mRotateExitAnimation != null;
-    }
-
-    private boolean stepAnimation(long now) {
-        if (now > mHalfwayPoint) {
-            mHalfwayPoint = Long.MAX_VALUE;
-        }
-        if (mFinishAnimReady && mFinishAnimStartTime < 0) {
-            mFinishAnimStartTime = now;
-        }
-
-        mMoreRotateExit = false;
-        if (mRotateExitAnimation != null) {
-            mMoreRotateExit = mRotateExitAnimation.getTransformation(now,
-                    mRotateExitTransformation);
-        }
-
-        mMoreRotateEnter = false;
-        if (mRotateEnterAnimation != null) {
-            mMoreRotateEnter = mRotateEnterAnimation.getTransformation(now,
-                    mRotateEnterTransformation);
-        }
-
-        if (!mMoreRotateExit) {
-            if (mRotateExitAnimation != null) {
-                mRotateExitAnimation.cancel();
-                mRotateExitAnimation = null;
-                mRotateExitTransformation.clear();
-            }
-        }
-
-        if (!mMoreRotateEnter) {
-            if (mRotateEnterAnimation != null) {
-                mRotateEnterAnimation.cancel();
-                mRotateEnterAnimation = null;
-                mRotateEnterTransformation.clear();
-            }
-        }
-
-        mExitTransformation.set(mRotateExitTransformation);
-        mEnterTransformation.set(mRotateEnterTransformation);
-
-        final boolean more = mMoreRotateEnter || mMoreRotateExit
-                || !mFinishAnimReady;
-
-        mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
-
-        return more;
-    }
-
-    void updateSurfaces(SurfaceControl.Transaction t) {
-        if (!mStarted) {
-            return;
-        }
-
-        if (mSurfaceControl != null) {
-            if (!mMoreRotateExit) {
-                t.hide(mSurfaceControl);
-            }
-        }
-
-        if (mExitingBlackFrame != null) {
-            if (!mMoreRotateExit) {
-                mExitingBlackFrame.hide(t);
-            } else {
-                mExitFrameFinalMatrix.setConcat(mExitTransformation.getMatrix(),
-                        mFrameInitialMatrix);
-                mExitingBlackFrame.setMatrix(t, mExitFrameFinalMatrix);
-                if (mForceDefaultOrientation) {
-                    mExitingBlackFrame.setAlpha(t, mExitTransformation.getAlpha());
-                }
-            }
-        }
-
-        if (mEnteringBlackFrame != null) {
-            if (!mMoreRotateEnter) {
-                mEnteringBlackFrame.hide(t);
-            } else {
-                mEnteringBlackFrame.setMatrix(t, mEnterTransformation.getMatrix());
-            }
-        }
-
-        t.setEarlyWakeup();
-        setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
-    }
-
-    public boolean stepAnimationLocked(long now) {
-        if (!hasAnimations()) {
-            mFinishAnimReady = false;
-            return false;
-        }
-
-        if (!mAnimRunning) {
-            if (mRotateEnterAnimation != null) {
-                mRotateEnterAnimation.setStartTime(now);
-            }
-            if (mRotateExitAnimation != null) {
-                mRotateExitAnimation.setStartTime(now);
-            }
-            mAnimRunning = true;
-            mHalfwayPoint = now + mRotateEnterAnimation.getDuration() / 2;
-        }
-
-        return stepAnimation(now);
-    }
-
     public Transformation getEnterTransformation() {
         return mEnterTransformation;
     }
+
+    /**
+     * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
+     * SurfaceAnimationRunner}.
+     * <p>
+     * The rotation animation is divided into the following hierarchy:
+     * <ul>
+     * <li> A first rotation layer, containing the blackframes. This layer is animated by the
+     * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
+     *     <ul>
+     *         <li> A child layer containing the screenshot on which is added an animation of it's
+     *     alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
+     *     </ul>
+     * <li> A second rotation layer used when custom animations are passed in
+     * {@link ScreenRotationAnimation#startAnimation(
+     *     SurfaceControl.Transaction, long, float, int, int, int, int)}.
+     * </ul>
+     * <p>
+     * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
+     * this three {@link SurfaceControl}s which then delegates the animation to the
+     * {@link ScreenRotationAnimation}.
+     */
+    class SurfaceRotationAnimationController {
+        private SurfaceAnimator mDisplayAnimator;
+        private SurfaceAnimator mEnterBlackFrameAnimator;
+        private SurfaceAnimator mScreenshotRotationAnimator;
+        private SurfaceAnimator mRotateScreenAnimator;
+        private final Runnable mNoopCallback = () -> { // b/141177184
+        };
+
+        /**
+         * Start the rotation animation of the display and the screenshot on the
+         * {@link SurfaceAnimationRunner}.
+         */
+        void startAnimation() {
+            mRotateScreenAnimator = startScreenshotAlphaAnimation();
+            mDisplayAnimator = startDisplayRotation();
+            if (mExitingBlackFrame != null) {
+                mScreenshotRotationAnimator = startScreenshotRotationAnimation();
+            }
+            if (mEnteringBlackFrame != null) {
+                mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
+            }
+        }
+
+        private SimpleSurfaceAnimatable.Builder initializeBuilder() {
+            return new SimpleSurfaceAnimatable.Builder()
+                    .setPendingTransactionSupplier(mDisplayContent::getPendingTransaction)
+                    .setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction)
+                    .setAnimationLeashSupplier(mDisplayContent::makeOverlay);
+        }
+
+        private SurfaceAnimator startDisplayRotation() {
+            return startAnimation(initializeBuilder()
+                            .setAnimationLeashParent(mDisplayContent.getSurfaceControl())
+                            .setSurfaceControl(mDisplayContent.getWindowingLayer())
+                            .setParentSurfaceControl(mDisplayContent.getSurfaceControl())
+                            .setWidth(mDisplayContent.getSurfaceWidth())
+                            .setHeight(mDisplayContent.getSurfaceHeight())
+                            .build(),
+                    createWindowAnimationSpec(mRotateEnterAnimation),
+                    this::cancel);
+        }
+
+        private SurfaceAnimator startScreenshotAlphaAnimation() {
+            return startAnimation(initializeBuilder()
+                            .setSurfaceControl(mSurfaceControl)
+                            .setAnimationLeashParent(mRotationLayer)
+                            .setWidth(mWidth)
+                            .setHeight(mHeight)
+                            .build(),
+                    createWindowAnimationSpec(mRotateAlphaAnimation),
+                    mNoopCallback);
+        }
+
+        private SurfaceAnimator startEnterBlackFrameAnimation() {
+            return startAnimation(initializeBuilder()
+                            .setSurfaceControl(mEnterBlackFrameLayer)
+                            .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+                            .build(),
+                    createWindowAnimationSpec(mRotateEnterAnimation),
+                    mNoopCallback);
+        }
+
+        private SurfaceAnimator startScreenshotRotationAnimation() {
+            return startAnimation(initializeBuilder()
+                            .setSurfaceControl(mRotationLayer)
+                            .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+                            .build(),
+                    createWindowAnimationSpec(mRotateExitAnimation),
+                    this::onAnimationEnd);
+        }
+
+        private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
+            return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
+                    false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
+        }
+
+        /**
+         * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
+         *
+         * @param animatable The animatable used for the animation.
+         * @param animationSpec                The spec of the animation.
+         * @param animationFinishedCallback    Callback passed to the {@link SurfaceAnimator} and
+         *                                    called when the animation finishes.
+         * @return The newly created {@link SurfaceAnimator} that as been started.
+         */
+        private SurfaceAnimator startAnimation(
+                SurfaceAnimator.Animatable animatable,
+                LocalAnimationAdapter.AnimationSpec animationSpec,
+                Runnable animationFinishedCallback) {
+            SurfaceAnimator animator = new SurfaceAnimator(
+                    animatable, animationFinishedCallback, mService);
+
+            LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
+                    animationSpec, mService.mSurfaceAnimationRunner);
+
+            animator.startAnimation(mDisplayContent.getPendingTransaction(),
+                    localAnimationAdapter, false);
+            return animator;
+        }
+
+        private void onAnimationEnd() {
+            mEnterBlackFrameAnimator = null;
+            mScreenshotRotationAnimator = null;
+            mRotateScreenAnimator = null;
+            mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
+            kill();
+            mService.updateRotation(false, false);
+            AccessibilityController accessibilityController = mService.mAccessibilityController;
+
+            if (accessibilityController != null) {
+                // We just finished rotation animation which means we did not
+                // announce the rotation and waited for it to end, announce now.
+                accessibilityController.onRotationChangedLocked(mDisplayContent);
+            }
+        }
+
+        public void cancel() {
+            if (mEnterBlackFrameAnimator != null) {
+                mEnterBlackFrameAnimator.cancelAnimation();
+            }
+
+            if (mScreenshotRotationAnimator != null) {
+                mScreenshotRotationAnimator.cancelAnimation();
+            }
+
+            if (mRotateScreenAnimator != null) {
+                mRotateScreenAnimator.cancelAnimation();
+            }
+
+            if (mDisplayAnimator != null) {
+                mDisplayAnimator.cancelAnimation();
+            }
+        }
+
+        public boolean isAnimating() {
+            return mDisplayAnimator != null && mDisplayAnimator.isAnimating()
+                    || mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating()
+                    || mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating()
+                    || mScreenshotRotationAnimator != null
+                    && mScreenshotRotationAnimator.isAnimating();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java b/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java
new file mode 100644
index 0000000..bf5d5e2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.SurfaceControl;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * An implementation of {@link SurfaceAnimator.Animatable} that is instantiated
+ * using a builder pattern for more convenience over reimplementing the whole interface.
+ * <p>
+ * Use {@link SimpleSurfaceAnimatable.Builder} to create a new instance of this class.
+ *
+ * @see com.android.server.wm.SurfaceAnimator.Animatable
+ */
+public class SimpleSurfaceAnimatable implements SurfaceAnimator.Animatable {
+    private final int mWidth;
+    private final int mHeight;
+    private final boolean mShouldDeferAnimationFinish;
+    private final SurfaceControl mAnimationLeashParent;
+    private final SurfaceControl mSurfaceControl;
+    private final SurfaceControl mParentSurfaceControl;
+    private final Runnable mCommitTransactionRunnable;
+    private final Supplier<SurfaceControl.Builder> mAnimationLeashFactory;
+    private final Supplier<SurfaceControl.Transaction> mPendingTransaction;
+    private final BiConsumer<SurfaceControl.Transaction, SurfaceControl> mOnAnimationLeashCreated;
+    private final Consumer<SurfaceControl.Transaction> mOnAnimationLeashLost;
+    private final Consumer<Runnable> mOnAnimationFinished;
+
+    /**
+     * Use {@link SimpleSurfaceAnimatable.Builder} to create a new instance.
+     */
+    private SimpleSurfaceAnimatable(Builder builder) {
+        mWidth = builder.mWidth;
+        mHeight = builder.mHeight;
+        mShouldDeferAnimationFinish = builder.mShouldDeferAnimationFinish;
+        mAnimationLeashParent = builder.mAnimationLeashParent;
+        mSurfaceControl = builder.mSurfaceControl;
+        mParentSurfaceControl = builder.mParentSurfaceControl;
+        mCommitTransactionRunnable = builder.mCommitTransactionRunnable;
+        mAnimationLeashFactory = builder.mAnimationLeashFactory;
+        mOnAnimationLeashCreated = builder.mOnAnimationLeashCreated;
+        mOnAnimationLeashLost = builder.mOnAnimationLeashLost;
+        mPendingTransaction = builder.mPendingTransactionSupplier;
+        mOnAnimationFinished = builder.mOnAnimationFinished;
+    }
+
+    @NonNull
+    @Override
+    public SurfaceControl.Transaction getPendingTransaction() {
+        return mPendingTransaction.get();
+    }
+
+    @Override
+    public void commitPendingTransaction() {
+        mCommitTransactionRunnable.run();
+    }
+
+    @Override
+    public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+        if (mOnAnimationLeashCreated != null) {
+            mOnAnimationLeashCreated.accept(t, leash);
+        }
+
+    }
+
+    @Override
+    public void onAnimationLeashLost(SurfaceControl.Transaction t) {
+        if (mOnAnimationLeashLost != null) {
+            mOnAnimationLeashLost.accept(t);
+        }
+    }
+
+    @Override
+    @NonNull
+    public SurfaceControl.Builder makeAnimationLeash() {
+        return mAnimationLeashFactory.get();
+    }
+
+    @Override
+    public SurfaceControl getAnimationLeashParent() {
+        return mAnimationLeashParent;
+    }
+
+    @Override
+    @Nullable
+    public SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
+    @Override
+    public SurfaceControl getParentSurfaceControl() {
+        return mParentSurfaceControl;
+    }
+
+    @Override
+    public int getSurfaceWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getSurfaceHeight() {
+        return mHeight;
+    }
+
+    @Override
+    public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+        if (mOnAnimationFinished != null) {
+            mOnAnimationFinished.accept(endDeferFinishCallback);
+        }
+        return mShouldDeferAnimationFinish;
+    }
+
+    /**
+     * Builder class to create a {@link SurfaceAnimator.Animatable} without having to
+     * create a new class that implements the interface.
+     */
+    static class Builder {
+        private int mWidth = -1;
+        private int mHeight = -1;
+        private boolean mShouldDeferAnimationFinish = false;
+
+        @Nullable
+        private SurfaceControl mAnimationLeashParent = null;
+
+        @Nullable
+        private SurfaceControl mSurfaceControl = null;
+
+        @Nullable
+        private SurfaceControl mParentSurfaceControl = null;
+        private Runnable mCommitTransactionRunnable;
+
+        @Nullable
+        private BiConsumer<SurfaceControl.Transaction, SurfaceControl> mOnAnimationLeashCreated =
+                null;
+
+        @Nullable
+        private Consumer<SurfaceControl.Transaction> mOnAnimationLeashLost = null;
+
+        @Nullable
+        private Consumer<Runnable> mOnAnimationFinished = null;
+
+        @NonNull
+        private Supplier<SurfaceControl.Transaction> mPendingTransactionSupplier;
+
+        @NonNull
+        private Supplier<SurfaceControl.Builder> mAnimationLeashFactory;
+
+        /**
+         * Set the runnable to be called when
+         * {@link SurfaceAnimator.Animatable#commitPendingTransaction()}
+         * is called.
+         *
+         * @see SurfaceAnimator.Animatable#commitPendingTransaction()
+         */
+        public SimpleSurfaceAnimatable.Builder setCommitTransactionRunnable(
+                @NonNull Runnable commitTransactionRunnable) {
+            mCommitTransactionRunnable = commitTransactionRunnable;
+            return this;
+        }
+
+        /**
+         * Set the callback called when
+         * {@link SurfaceAnimator.Animatable#onAnimationLeashCreated(SurfaceControl.Transaction,
+         * SurfaceControl)} is called
+         *
+         * @see SurfaceAnimator.Animatable#onAnimationLeashCreated(SurfaceControl.Transaction,
+         * SurfaceControl)
+         */
+        public SimpleSurfaceAnimatable.Builder setOnAnimationLeashCreated(
+                @Nullable BiConsumer<SurfaceControl.Transaction, SurfaceControl>
+                        onAnimationLeashCreated) {
+            mOnAnimationLeashCreated = onAnimationLeashCreated;
+            return this;
+        }
+
+        /**
+         * Set the callback called when
+         * {@link SurfaceAnimator.Animatable#onAnimationLeashLost(SurfaceControl.Transaction)}
+         * (SurfaceControl.Transaction, SurfaceControl)} is called
+         *
+         * @see SurfaceAnimator.Animatable#onAnimationLeashLost(SurfaceControl.Transaction)
+         */
+        public SimpleSurfaceAnimatable.Builder setOnAnimationLeashLost(
+                @Nullable Consumer<SurfaceControl.Transaction> onAnimationLeashLost) {
+            mOnAnimationLeashLost = onAnimationLeashLost;
+            return this;
+        }
+
+        /**
+         * @see SurfaceAnimator.Animatable#getPendingTransaction()
+         */
+        public Builder setPendingTransactionSupplier(
+                @NonNull Supplier<SurfaceControl.Transaction> pendingTransactionSupplier) {
+            mPendingTransactionSupplier = pendingTransactionSupplier;
+            return this;
+        }
+
+        /**
+         * Set the {@link Supplier} responsible for creating a new animation leash.
+         *
+         * @see SurfaceAnimator.Animatable#makeAnimationLeash()
+         */
+        public SimpleSurfaceAnimatable.Builder setAnimationLeashSupplier(
+                @NonNull Supplier<SurfaceControl.Builder> animationLeashFactory) {
+            mAnimationLeashFactory = animationLeashFactory;
+            return this;
+        }
+
+        /**
+         * @see SurfaceAnimator.Animatable#getAnimationLeashParent()
+         */
+        public SimpleSurfaceAnimatable.Builder setAnimationLeashParent(
+                SurfaceControl animationLeashParent) {
+            mAnimationLeashParent = animationLeashParent;
+            return this;
+        }
+
+        /**
+         * @see SurfaceAnimator.Animatable#getSurfaceControl()
+         */
+        public SimpleSurfaceAnimatable.Builder setSurfaceControl(
+                @NonNull SurfaceControl surfaceControl) {
+            mSurfaceControl = surfaceControl;
+            return this;
+        }
+
+        /**
+         * @see SurfaceAnimator.Animatable#getParentSurfaceControl()
+         */
+        public SimpleSurfaceAnimatable.Builder setParentSurfaceControl(
+                SurfaceControl parentSurfaceControl) {
+            mParentSurfaceControl = parentSurfaceControl;
+            return this;
+        }
+
+        /**
+         * Default to -1.
+         *
+         * @see SurfaceAnimator.Animatable#getSurfaceWidth()
+         */
+        public SimpleSurfaceAnimatable.Builder setWidth(int width) {
+            mWidth = width;
+            return this;
+        }
+
+        /**
+         * Default to -1.
+         *
+         * @see SurfaceAnimator.Animatable#getSurfaceHeight()
+         */
+        public SimpleSurfaceAnimatable.Builder setHeight(int height) {
+            mHeight = height;
+            return this;
+        }
+
+        /**
+         * Set the value returned by
+         * {@link SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)}.
+         *
+         * @param onAnimationFinish will be called with the runnable to execute when the animation
+         *                          needs to be finished.
+         * @see SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)
+         */
+        public SimpleSurfaceAnimatable.Builder setShouldDeferAnimationFinish(
+                boolean shouldDeferAnimationFinish,
+                @Nullable Consumer<Runnable> onAnimationFinish) {
+            mShouldDeferAnimationFinish = shouldDeferAnimationFinish;
+            mOnAnimationFinished = onAnimationFinish;
+            return this;
+        }
+
+        public SurfaceAnimator.Animatable build() {
+            if (mPendingTransactionSupplier == null) {
+                throw new IllegalArgumentException("mPendingTransactionSupplier cannot be null");
+            }
+            if (mAnimationLeashFactory == null) {
+                throw new IllegalArgumentException("mAnimationLeashFactory cannot be null");
+            }
+            if (mCommitTransactionRunnable == null) {
+                throw new IllegalArgumentException("mCommitTransactionRunnable cannot be null");
+            }
+            if (mSurfaceControl == null) {
+                throw new IllegalArgumentException("mSurfaceControl cannot be null");
+            }
+            return new SimpleSurfaceAnimatable(this);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 42866f9..fc8a27d 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -50,6 +50,7 @@
 import android.view.InputEvent;
 import android.view.InputWindowHandle;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -302,7 +303,8 @@
         mDisplayContent.getDisplayRotation().pause();
 
         // Notify InputMonitor to take mDragWindowHandle.
-        mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+        mDisplayContent.getInputMonitor().updateInputWindowsImmediately();
+        new SurfaceControl.Transaction().syncInputWindows().apply(true);
 
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 2441954..56b3bba 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -24,7 +24,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -51,7 +50,6 @@
     private @Nullable TaskPositioner mTaskPositioner;
 
     private final Rect mTmpClipRect = new Rect();
-    private IBinder mTransferTouchFromToken;
 
     boolean isPositioningLocked() {
         return mTaskPositioner != null;
@@ -104,8 +102,6 @@
 
         mTmpClipRect.set(0, 0, p.x, p.y);
         t.setWindowCrop(mInputSurface, mTmpClipRect);
-        t.transferTouchFocus(mTransferTouchFromToken, h.token);
-        mTransferTouchFromToken = null;
     }
 
     boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -168,6 +164,7 @@
         mPositioningDisplay = displayContent;
 
         mTaskPositioner = TaskPositioner.create(mService);
+        mTaskPositioner.register(displayContent);
 
         // We need to grab the touch focus so that the touch events during the
         // resizing/scrolling are not sent to the app. 'win' is the main window
@@ -178,8 +175,12 @@
                 && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
             transferFocusFromWin = displayContent.mCurrentFocus;
         }
-        mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken();
-        mTaskPositioner.register(displayContent);
+        if (!mInputManager.transferTouchFocus(
+                transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
+            Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
+            cleanUpTaskPositioner();
+            return false;
+        }
 
         mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
         return true;
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index d36ebf0..f291573 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -38,8 +38,8 @@
     private int mWidth;
     private int mHeight;
 
-    TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory, Task task,
-            SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
+    TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory,
+            Task task, SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
         GraphicBuffer buffer = screenshotBuffer == null
                 ? null : screenshotBuffer.getGraphicBuffer();
         mTask = task;
@@ -58,6 +58,8 @@
             surface.copyFrom(mSurfaceControl);
             surface.attachAndQueueBufferWithColorSpace(buffer, screenshotBuffer.getColorSpace());
             surface.release();
+            final float scale = 1.0f * mTask.getBounds().width() / mWidth;
+            mSurfaceControl.setMatrix(scale, 0, 0, scale);
         }
         getPendingTransaction().show(mSurfaceControl);
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index c7916e8..1e206f6 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -21,7 +21,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 
 import android.content.Context;
 import android.os.Trace;
@@ -108,14 +107,6 @@
     }
 
     void removeDisplayLocked(final int displayId) {
-        final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
-        if (displayAnimator != null) {
-            if (displayAnimator.mScreenRotationAnimation != null) {
-                displayAnimator.mScreenRotationAnimation.kill();
-                displayAnimator.mScreenRotationAnimation = null;
-            }
-        }
-
         mDisplayContentsAnimators.delete(displayId);
     }
 
@@ -156,27 +147,6 @@
                 for (int i = 0; i < numDisplays; i++) {
                     final int displayId = mDisplayContentsAnimators.keyAt(i);
                     final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-                    DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
-
-                    final ScreenRotationAnimation screenRotationAnimation =
-                            displayAnimator.mScreenRotationAnimation;
-                    if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
-                        if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
-                            setAnimating(true);
-                        } else {
-                            mBulkUpdateParams |= SET_UPDATE_ROTATION;
-                            screenRotationAnimation.kill();
-                            displayAnimator.mScreenRotationAnimation = null;
-
-                            // display.
-                            if (accessibilityController != null) {
-                                // We just finished rotation animation which means we did not
-                                // announce the rotation and waited for it to end, announce now.
-                                accessibilityController.onRotationChangedLocked(dc);
-                            }
-                        }
-                    }
-
                     // Update animations of all applications, including those
                     // associated with exiting/removed apps
                     dc.updateWindowsForAnimator();
@@ -188,12 +158,6 @@
                     final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
 
                     dc.checkAppWindowsReadyToShow();
-
-                    final ScreenRotationAnimation screenRotationAnimation =
-                            mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
-                    if (screenRotationAnimation != null) {
-                        screenRotationAnimation.updateSurfaces(mTransaction);
-                    }
                     orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
                     if (accessibilityController != null) {
                         accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId);
@@ -273,22 +237,14 @@
 
     public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
         final String subPrefix = "  " + prefix;
-        final String subSubPrefix = "  " + subPrefix;
 
         for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
             pw.print(prefix); pw.print("DisplayContentsAnimator #");
                     pw.print(mDisplayContentsAnimators.keyAt(i));
                     pw.println(":");
-            final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
             final DisplayContent dc =
                     mService.mRoot.getDisplayContent(mDisplayContentsAnimators.keyAt(i));
             dc.dumpWindowAnimators(pw, subPrefix);
-            if (displayAnimator.mScreenRotationAnimation != null) {
-                pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
-                displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
-            } else if (dumpAll) {
-                pw.print(subPrefix); pw.println("no ScreenRotationAnimation ");
-            }
             pw.println();
         }
 
@@ -322,23 +278,6 @@
         return displayAnimator;
     }
 
-    void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
-        final DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
-
-        if (animator != null) {
-            animator.mScreenRotationAnimation = animation;
-        }
-    }
-
-    ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) {
-        if (displayId < 0) {
-            return null;
-        }
-
-        DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
-        return animator != null? animator.mScreenRotationAnimation : null;
-    }
-
     void requestRemovalOfReplacedWindows(WindowState win) {
         mRemoveReplacedWindows = true;
     }
@@ -358,7 +297,6 @@
     }
 
     private class DisplayContentsAnimator {
-        ScreenRotationAnimation mScreenRotationAnimation = null;
     }
 
     boolean isAnimating() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index aa2a964..abbd1ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -161,9 +161,8 @@
         default boolean registerInputChannel(
                 DragState state, Display display, InputManagerService service,
                 InputChannel source) {
-            state.mTransferTouchFromToken = source.getToken();
             state.register(display);
-            return true;
+            return service.transferTouchFocus(source, state.getInputChannel());
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b4309c7..169e415 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5392,7 +5392,7 @@
         mExitAnimId = exitAnim;
         mEnterAnimId = enterAnim;
         ScreenRotationAnimation screenRotationAnimation =
-                mAnimator.getScreenRotationAnimationLocked(mFrozenDisplayId);
+                displayContent.getRotationAnimation();
         if (screenRotationAnimation != null) {
             screenRotationAnimation.kill();
         }
@@ -5404,8 +5404,7 @@
         screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                 displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
                 this);
-        mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
-                screenRotationAnimation);
+        displayContent.setRotationAnimation(screenRotationAnimation);
     }
 
     void stopFreezingDisplayLocked() {
@@ -5456,8 +5455,8 @@
 
         boolean updateRotation = false;
 
-        ScreenRotationAnimation screenRotationAnimation =
-                mAnimator.getScreenRotationAnimationLocked(displayId);
+        ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null
+                : displayContent.getRotationAnimation();
         if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
             if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
@@ -5470,16 +5469,15 @@
                     getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                         displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                 mTransaction.apply();
-                scheduleAnimationLocked();
             } else {
                 screenRotationAnimation.kill();
-                mAnimator.setScreenRotationAnimationLocked(displayId, null);
+                displayContent.setRotationAnimation(null);
                 updateRotation = true;
             }
         } else {
             if (screenRotationAnimation != null) {
                 screenRotationAnimation.kill();
-                mAnimator.setScreenRotationAnimationLocked(displayId, null);
+                displayContent.setRotationAnimation(null);
             }
             updateRotation = true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c676e72..8fccc8d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -673,9 +673,8 @@
     }
 
     void computeShownFrameLocked() {
-        final int displayId = mWin.getDisplayId();
         final ScreenRotationAnimation screenRotationAnimation =
-                mAnimator.getScreenRotationAnimationLocked(displayId);
+                mWin.getDisplayContent().getRotationAnimation();
         final boolean windowParticipatesInScreenRotationAnimation =
                 !mWin.mForceSeamlesslyRotate;
         final boolean screenAnimation = screenRotationAnimation != null
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index d5fbd2b..7aa9c7c 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -56,37 +56,57 @@
     return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
 }
 
-// Helper used to transparently deal with the vibrator HAL becoming unavailable.
+template <typename I>
+class HalWrapper {
+  public:
+    static std::unique_ptr<HalWrapper> Create() {
+        // Assume that if getService returns a nullptr, HAL is not available on the
+        // device.
+        auto hal = I::getService();
+        return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
+    }
+
+    // Helper used to transparently deal with the vibrator HAL becoming unavailable.
+    template<class R, class... Args0, class... Args1>
+    Return<R> call(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
+        // Return<R> doesn't have a default constructor, so make a Return<R> with
+        // STATUS::EX_NONE.
+        using ::android::hardware::Status;
+        Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
+
+        // Note that ret is guaranteed to be changed after this loop.
+        for (int i = 0; i < NUM_TRIES; ++i) {
+            ret = (mHal == nullptr) ? NullptrStatus<R>()
+                    : (*mHal.*fn)(std::forward<Args1>(args1)...);
+
+            if (ret.isOk()) {
+                break;
+            }
+
+            ALOGE("Failed to issue command to vibrator HAL. Retrying.");
+            // Restoring connection to the HAL.
+            mHal = I::tryGetService();
+        }
+        return ret;
+    }
+
+  private:
+    HalWrapper(sp<I> &&hal) : mHal(std::move(hal)) {}
+
+  private:
+    sp<I> mHal;
+};
+
+template <typename I>
+static auto getHal() {
+    static auto sHalWrapper = HalWrapper<I>::Create();
+    return sHalWrapper.get();
+}
+
 template<class R, class I, class... Args0, class... Args1>
 Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
-    // Assume that if getService returns a nullptr, HAL is not available on the
-    // device.
-    static sp<I> sHal = I::getService();
-    static bool sAvailable = sHal != nullptr;
-
-    if (!sAvailable) {
-        return NullptrStatus<R>();
-    }
-
-    // Return<R> doesn't have a default constructor, so make a Return<R> with
-    // STATUS::EX_NONE.
-    using ::android::hardware::Status;
-    Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
-
-    // Note that ret is guaranteed to be changed after this loop.
-    for (int i = 0; i < NUM_TRIES; ++i) {
-        ret = (sHal == nullptr) ? NullptrStatus<R>()
-                : (*sHal.*fn)(std::forward<Args1>(args1)...);
-
-        if (ret.isOk()) {
-            break;
-        }
-
-        ALOGE("Failed to issue command to vibrator HAL. Retrying.");
-        // Restoring connection to the HAL.
-        sHal = I::tryGetService();
-    }
-    return ret;
+    auto hal = getHal<I>();
+    return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
 }
 
 template<class R>
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 03f4755..fb2fdab1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -159,12 +159,7 @@
     status_t status = android_view_PointerIcon_loadSystemIcon(env,
             contextObj, style, outPointerIcon);
     if (!status) {
-        SkBitmap* bitmapCopy = &outSpriteIcon->bitmap;
-        SkImageInfo bitmapCopyInfo = outPointerIcon->bitmap.info().makeColorType(kN32_SkColorType);
-        if (bitmapCopy->tryAllocPixels(bitmapCopyInfo)) {
-            outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
-                    bitmapCopy->rowBytes(), 0, 0);
-        }
+        outSpriteIcon->bitmap = outPointerIcon->bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888);
         outSpriteIcon->style = outPointerIcon->style;
         outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
         outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
@@ -1575,6 +1570,27 @@
     im->setSystemUiVisibility(visibility);
 }
 
+static jboolean nativeTransferTouchFocus(JNIEnv* env,
+        jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    sp<InputChannel> fromChannel =
+            android_view_InputChannel_getInputChannel(env, fromChannelObj);
+    sp<InputChannel> toChannel =
+            android_view_InputChannel_getInputChannel(env, toChannelObj);
+
+    if (fromChannel == nullptr || toChannel == nullptr) {
+        return JNI_FALSE;
+    }
+
+    if (im->getInputManager()->getDispatcher()->
+            transferTouchFocus(fromChannel->getToken(), toChannel->getToken())) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
 static void nativeSetPointerSpeed(JNIEnv* /* env */,
         jclass /* clazz */, jlong ptr, jint speed) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1709,15 +1725,8 @@
         return;
     }
 
-    SpriteIcon spriteIcon;
-    SkImageInfo spriteInfo = pointerIcon.bitmap.info().makeColorType(kN32_SkColorType);
-    if (spriteIcon.bitmap.tryAllocPixels(spriteInfo)) {
-        pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
-                spriteIcon.bitmap.rowBytes(), 0, 0);
-    }
-    spriteIcon.style = pointerIcon.style;
-    spriteIcon.hotSpotX = pointerIcon.hotSpotX;
-    spriteIcon.hotSpotY = pointerIcon.hotSpotY;
+    SpriteIcon spriteIcon(pointerIcon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888),
+                          pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
     im->setCustomPointerIcon(spriteIcon);
 }
 
@@ -1775,6 +1784,8 @@
             (void*) nativeSetInputDispatchMode },
     { "nativeSetSystemUiVisibility", "(JI)V",
             (void*) nativeSetSystemUiVisibility },
+    { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+            (void*) nativeTransferTouchFocus },
     { "nativeSetPointerSpeed", "(JI)V",
             (void*) nativeSetPointerSpeed },
     { "nativeSetShowTouches", "(JZ)V",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 479dd1e..c3ff285 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -126,6 +126,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.SecurityLog;
@@ -507,6 +508,7 @@
     private final OverlayPackagesProvider mOverlayPackagesProvider;
 
     private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
+    private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
 
     /**
      * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
@@ -2295,6 +2297,9 @@
                 policy = new DevicePolicyData(userHandle);
                 mUserData.append(userHandle, policy);
                 loadSettingsLocked(policy, userHandle);
+                if (userHandle == UserHandle.USER_SYSTEM) {
+                    mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
+                }
             }
             return policy;
         }
@@ -8958,6 +8963,8 @@
             pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
             pw.println();
             mPolicyCache.dump(pw);
+            pw.println();
+            mStateCache.dump(pw);
         }
     }
 
@@ -11169,6 +11176,9 @@
                 DevicePolicyData policy = getUserData(userHandle);
                 if (!policy.mUserSetupComplete) {
                     policy.mUserSetupComplete = true;
+                    if (userHandle == UserHandle.USER_SYSTEM) {
+                        mStateCache.setDeviceProvisioned(true);
+                    }
                     synchronized (getLockObject()) {
                         saveSettingsLocked(userHandle);
                     }
@@ -11481,6 +11491,12 @@
         protected DevicePolicyCache getDevicePolicyCache() {
             return mPolicyCache;
         }
+
+        @Override
+        protected DeviceStateCache getDeviceStateCache() {
+            return mStateCache;
+        }
+
     }
 
     private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -13021,6 +13037,7 @@
                 Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
         DevicePolicyData policy = getUserData(userId);
         policy.mUserSetupComplete = isUserCompleted;
+        mStateCache.setDeviceProvisioned(isUserCompleted);
         synchronized (getLockObject()) {
             saveSettingsLocked(userId);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
new file mode 100644
index 0000000..c3cb9b0
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceStateCache;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * Implementation of {@link DeviceStateCache}, to which {@link DevicePolicyManagerService} pushes
+ * device state.
+ *
+ */
+public class DeviceStateCacheImpl extends DeviceStateCache {
+    /**
+     * Lock object. For simplicity we just always use this as the lock. We could use each object
+     * as a lock object to make it more fine-grained, but that'd make copy-paste error-prone.
+     */
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private boolean mIsDeviceProvisioned = false;
+
+    @Override
+    public boolean isDeviceProvisioned() {
+        return mIsDeviceProvisioned;
+    }
+
+    /** Update the device provisioned flag for USER_SYSTEM */
+    public void setDeviceProvisioned(boolean provisioned) {
+        synchronized (mLock) {
+            mIsDeviceProvisioned = provisioned;
+        }
+    }
+
+    /** Dump content */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("Device state cache:");
+        pw.increaseIndent();
+        pw.println("Device provisioned: " + mIsDeviceProvisioned);
+        pw.decreaseIndent();
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 90d0c30..e93e17a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -141,7 +141,6 @@
 import com.android.server.security.KeyChainSystemService;
 import com.android.server.signedconfig.SignedConfigService;
 import com.android.server.soundtrigger.SoundTriggerService;
-import com.android.server.stats.StatsCompanionService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
 import com.android.server.telecom.TelecomLoaderService;
@@ -202,6 +201,8 @@
             "com.android.server.print.PrintManagerService";
     private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
             "com.android.server.companion.CompanionDeviceManagerService";
+    private static final String STATS_COMPANION_SERVICE_LIFECYCLE_CLASS =
+            "com.android.server.stats.StatsCompanionService$Lifecycle";
     private static final String USB_SERVICE_CLASS =
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
@@ -1875,7 +1876,7 @@
 
         // Statsd helper
         t.traceBegin("StartStatsCompanionService");
-        mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
+        mSystemServiceManager.startService(STATS_COMPANION_SERVICE_LIFECYCLE_CLASS);
         t.traceEnd();
 
         // Incidentd and dumpstated helper
diff --git a/services/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
index af9e601..31a2556 100644
--- a/services/net/java/android/net/netlink/InetDiagMessage.java
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -16,26 +16,23 @@
 
 package android.net.netlink;
 
-import static android.os.Process.INVALID_UID;
 import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
 import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.NETLINK_INET_DIAG;
 
-import android.os.Build;
-import android.os.Process;
+import android.net.util.SocketUtils;
 import android.system.ErrnoException;
 import android.util.Log;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.net.DatagramSocket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetSocketAddress;
@@ -163,17 +160,25 @@
      */
     public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
                                             InetSocketAddress remote) {
+        int uid = INVALID_UID;
+        FileDescriptor fd = null;
         try {
-            final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
+            fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
             NetlinkSocket.connectToKernel(fd);
-
-            return lookupUid(protocol, local, remote, fd);
-
+            uid = lookupUid(protocol, local, remote, fd);
         } catch (ErrnoException | SocketException | IllegalArgumentException
                 | InterruptedIOException e) {
             Log.e(TAG, e.toString());
+        } finally {
+            if (fd != null) {
+                try {
+                    SocketUtils.closeSocket(fd);
+                } catch (IOException e) {
+                    Log.e(TAG, e.toString());
+                }
+            }
         }
-        return INVALID_UID;
+        return uid;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 9e3b54d..c3a1243 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,10 +16,8 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
-import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
-import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
+import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
 
@@ -30,7 +28,6 @@
 
 import org.junit.Test;
 
-import java.io.ByteArrayOutputStream;
 import java.util.Collections;
 
 /**
@@ -99,7 +96,7 @@
             "0",
             "2222", // this in start time (in ticks per second)
             "1257177088",
-            "3",
+            "3", // this is RSS in pages
             "4294967295",
             "2936971264",
             "2936991289",
@@ -129,53 +126,6 @@
             "3198889956",
             "0");
 
-    private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
-            + "State:\tS (sleeping)\n"
-            + "Tgid:\t12088\n"
-            + "Pid:\t12088\n"
-            + "PPid:\t723\n"
-            + "TracerPid:\t0\n"
-            + "Uid:\t10083\t10083\t10083\t10083\n"
-            + "Gid:\t10083\t10083\t10083\t10083\n"
-            + "Ngid:\t0\n"
-            + "FDSize:\t128\n"
-            + "Groups:\t3003 9997 20083 50083 \n"
-            + "VmPeak:\t 4546844 kB\n"
-            + "VmSize:\t 4542636 kB\n"
-            + "VmLck:\t       0 kB\n"
-            + "VmPin:\t       0 kB\n"
-            + "VmHWM:\t  137668 kB\n" // RSS high-water mark
-            + "VmRSS:\t  126776 kB\n" // RSS
-            + "RssAnon:\t   37860 kB\n"
-            + "RssFile:\t   88764 kB\n"
-            + "RssShmem:\t     152 kB\n"
-            + "VmData:\t 4125112 kB\n"
-            + "VmStk:\t    8192 kB\n"
-            + "VmExe:\t      24 kB\n"
-            + "VmLib:\t  102432 kB\n"
-            + "VmPTE:\t    1300 kB\n"
-            + "VmPMD:\t      36 kB\n"
-            + "VmSwap:\t      22 kB\n" // Swap
-            + "Threads:\t95\n"
-            + "SigQ:\t0/13641\n"
-            + "SigPnd:\t0000000000000000\n"
-            + "ShdPnd:\t0000000000000000\n"
-            + "SigBlk:\t0000000000001204\n"
-            + "SigIgn:\t0000000000000001\n"
-            + "SigCgt:\t00000006400084f8\n"
-            + "CapInh:\t0000000000000000\n"
-            + "CapPrm:\t0000000000000000\n"
-            + "CapEff:\t0000000000000000\n"
-            + "CapBnd:\t0000000000000000\n"
-            + "CapAmb:\t0000000000000000\n"
-            + "Seccomp:\t2\n"
-            + "Cpus_allowed:\tff\n"
-            + "Cpus_allowed_list:\t0-7\n"
-            + "Mems_allowed:\t1\n"
-            + "Mems_allowed_list:\t0\n"
-            + "voluntary_ctxt_switches:\t903\n"
-            + "nonvoluntary_ctxt_switches:\t104\n";
-
     @Test
     public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
         MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
@@ -197,71 +147,26 @@
 
     @Test
     public void testParseMemoryStatFromProcfs_parsesCorrectValues() {
-        MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, PROC_STATUS_CONTENTS);
+        MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
         assertEquals(1, stat.pgfault);
         assertEquals(2, stat.pgmajfault);
-        assertEquals(126776 * BYTES_IN_KILOBYTE, stat.rssInBytes);
+        assertEquals(3 * PAGE_SIZE, stat.rssInBytes);
         assertEquals(0, stat.cacheInBytes);
-        assertEquals(22 * BYTES_IN_KILOBYTE, stat.swapInBytes);
-        assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos);
-        assertEquals(37860 * BYTES_IN_KILOBYTE, stat.anonRssInBytes);
+        assertEquals(0, stat.swapInBytes);
     }
 
     @Test
     public void testParseMemoryStatFromProcfs_emptyContents() {
-        MemoryStat stat = parseMemoryStatFromProcfs("", PROC_STATUS_CONTENTS);
+        MemoryStat stat = parseMemoryStatFromProcfs("");
         assertNull(stat);
 
-        stat = parseMemoryStatFromProcfs(null, PROC_STATUS_CONTENTS);
-        assertNull(stat);
-
-        stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, "");
-        assertNull(stat);
-
-        stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, null);
+        stat = parseMemoryStatFromProcfs(null);
         assertNull(stat);
     }
 
     @Test
     public void testParseMemoryStatFromProcfs_invalidValue() {
         String contents = String.join(" ", Collections.nCopies(24, "memory"));
-        assertNull(parseMemoryStatFromProcfs(contents, PROC_STATUS_CONTENTS));
-    }
-
-    @Test
-    public void testParseCmdlineFromProcfs_invalidValue() {
-        byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
-
-        assertEquals("", parseCmdlineFromProcfs(bytesToString(nothing)));
-    }
-
-    @Test
-    public void testParseCmdlineFromProcfs_correctValue_noNullBytes() {
-        assertEquals("com.google.app", parseCmdlineFromProcfs("com.google.app"));
-    }
-
-    @Test
-    public void testParseCmdlineFromProcfs_correctValue_withNullBytes() {
-        byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
-
-        assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
-
-        // test\0\0test
-        byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
-
-        assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
-    }
-
-    @Test
-    public void testParseCmdlineFromProcfs_emptyContents() {
-        assertEquals("", parseCmdlineFromProcfs(""));
-
-        assertEquals("", parseCmdlineFromProcfs(null));
-    }
-
-    private static String bytesToString(byte[] bytes) {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-        output.write(bytes, 0, bytes.length);
-        return output.toString();
+        assertNull(parseMemoryStatFromProcfs(contents));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 1f5ebe4..7cece1f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -29,6 +29,7 @@
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
 import android.app.trust.TrustManager;
 import android.content.ComponentName;
 import android.content.pm.UserInfo;
@@ -37,6 +38,7 @@
 import android.os.IProgressListener;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.security.KeyStore;
@@ -91,6 +93,8 @@
     FakeGsiService mGsiService;
     PasswordSlotManagerTestable mPasswordSlotManager;
     RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+    UserManagerInternal mUserManagerInternal;
+    DeviceStateCache mDeviceStateCache;
     protected boolean mHasSecureLockScreen;
 
     @Override
@@ -108,6 +112,8 @@
         mGsiService = new FakeGsiService();
         mPasswordSlotManager = new PasswordSlotManagerTestable();
         mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
+        mUserManagerInternal = mock(UserManagerInternal.class);
+        mDeviceStateCache = mock(DeviceStateCache.class);
 
         LocalServices.removeServiceForTest(LockSettingsInternal.class);
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -144,7 +150,8 @@
         mAuthSecretService = mock(IAuthSecret.class);
         mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
                 mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
-                mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
+                mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
+                mUserManagerInternal, mDeviceStateCache);
         when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
         mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
         installChildProfile(MANAGED_PROFILE_USER_ID);
@@ -172,6 +179,9 @@
         // Adding a fake Device Owner app which will enable escrow token support in LSS.
         when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
                 new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
+        when(mUserManagerInternal.isDeviceManaged()).thenReturn(true);
+        when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+
         mLocalService = LocalServices.getService(LockSettingsInternal.class);
     }
 
@@ -184,6 +194,7 @@
         when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
         when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true);
         when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true);
+        when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true);
         return userInfo;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 5c67d04..65d6f45 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -19,12 +19,14 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.IActivityManager;
+import android.app.admin.DeviceStateCache;
 import android.content.Context;
 import android.hardware.authsecret.V1_0.IAuthSecret;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.security.KeyStore;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -44,15 +46,16 @@
         private LockPatternUtils mLockPatternUtils;
         private IStorageManager mStorageManager;
         private SyntheticPasswordManager mSpManager;
-        private IAuthSecret mAuthSecretService;
         private FakeGsiService mGsiService;
         private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+        private UserManagerInternal mUserManagerInternal;
+        private DeviceStateCache mDeviceStateCache;
 
         public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
                 IActivityManager activityManager, LockPatternUtils lockPatternUtils,
                 IStorageManager storageManager, SyntheticPasswordManager spManager,
-                IAuthSecret authSecretService, FakeGsiService gsiService,
-                RecoverableKeyStoreManager recoverableKeyStoreManager) {
+                FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+                UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
             super(context);
             mLockSettingsStorage = storage;
             mKeyStore = keyStore;
@@ -62,6 +65,8 @@
             mSpManager = spManager;
             mGsiService = gsiService;
             mRecoverableKeyStoreManager = recoverableKeyStoreManager;
+            mUserManagerInternal = userManagerInternal;
+            mDeviceStateCache = deviceStateCache;
         }
 
         @Override
@@ -93,6 +98,10 @@
         public LockPatternUtils getLockPatternUtils() {
             return mLockPatternUtils;
         }
+        @Override
+        public DeviceStateCache getDeviceStateCache() {
+            return mDeviceStateCache;
+        }
 
         @Override
         public KeyStore getKeyStore() {
@@ -110,6 +119,11 @@
         }
 
         @Override
+        public UserManagerInternal getUserManagerInternal() {
+            return mUserManagerInternal;
+        }
+
+        @Override
         public boolean hasEnrolledBiometrics(int userId) {
             return false;
         }
@@ -134,10 +148,11 @@
             LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
             IStorageManager storageManager, IActivityManager mActivityManager,
             SyntheticPasswordManager spManager, IAuthSecret authSecretService,
-            FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
+            FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+            UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
         super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
-                storageManager, spManager, authSecretService, gsiService,
-                recoverableKeyStoreManager));
+                storageManager, spManager, gsiService,
+                recoverableKeyStoreManager, userManagerInternal, deviceStateCache));
         mGateKeeperService = gatekeeper;
         mAuthSecretService = authSecretService;
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 127cf49..0776589 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.admin.PasswordMetrics;
 import android.os.RemoteException;
@@ -468,6 +469,18 @@
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
     }
 
+    public void testEscrowTokenCannotBeActivatedOnUnmanagedUser() {
+        final byte[] token = "some-high-entropy-secure-token".getBytes();
+        when(mUserManagerInternal.isDeviceManaged()).thenReturn(false);
+        when(mUserManagerInternal.isUserManaged(PRIMARY_USER_ID)).thenReturn(false);
+        when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+
+        try {
+            mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
+            fail("Escrow token should not be possible on unmanaged device");
+        } catch (SecurityException expected) { }
+    }
+
     public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
         final byte[] password = "password".getBytes();
         final byte[] pattern = "123654".getBytes();
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 819091c..e020945 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -27,10 +27,13 @@
 
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsingPackage;
 import android.os.Build;
 import android.os.Process;
 import android.permission.IPermissionManager;
@@ -56,45 +59,55 @@
     @Mock
     AppsFilter.FeatureConfig mFeatureConfigMock;
 
-    private Map<String, PackageParser.Package> mExisting = new ArrayMap<>();
+    private Map<String, AndroidPackage> mExisting = new ArrayMap<>();
 
-    private static PackageBuilder pkg(String packageName) {
-        return new PackageBuilder(packageName)
-                .setApplicationInfoTargetSdkVersion(Build.VERSION_CODES.R);
+    private static ParsingPackage pkg(String packageName) {
+        return PackageImpl.forParsing(packageName)
+                .setTargetSdkVersion(Build.VERSION_CODES.R);
     }
 
-    private static PackageBuilder pkg(String packageName, Intent... queries) {
-        return pkg(packageName).setQueriesIntents(queries);
-    }
-
-    private static PackageBuilder pkg(String packageName, String... queriesPackages) {
-        return pkg(packageName).setQueriesPackages(queriesPackages);
-    }
-
-    private static PackageBuilder pkg(String packageName, IntentFilter... filters) {
-        final PackageBuilder packageBuilder = pkg(packageName).addActivity(
-                pkg -> new PackageParser.ParseComponentArgs(pkg, new String[1], 0, 0, 0, 0, 0, 0,
-                        new String[]{packageName}, 0, 0, 0), new ActivityInfo());
-        for (IntentFilter filter : filters) {
-            packageBuilder.addActivityIntentInfo(0 /* index */, activity -> {
-                final PackageParser.ActivityIntentInfo info =
-                        new PackageParser.ActivityIntentInfo(activity);
-                if (filter.countActions() > 0) {
-                    filter.actionsIterator().forEachRemaining(info::addAction);
-                }
-                if (filter.countCategories() > 0) {
-                    filter.actionsIterator().forEachRemaining(info::addAction);
-                }
-                if (filter.countDataAuthorities() > 0) {
-                    filter.authoritiesIterator().forEachRemaining(info::addDataAuthority);
-                }
-                if (filter.countDataSchemes() > 0) {
-                    filter.schemesIterator().forEachRemaining(info::addDataScheme);
-                }
-                return info;
-            });
+    private static ParsingPackage pkg(String packageName, Intent... queries) {
+        ParsingPackage pkg = pkg(packageName);
+        if (queries != null) {
+            for (Intent intent : queries) {
+                pkg.addQueriesIntent(intent);
+            }
         }
-        return packageBuilder;
+        return pkg;
+    }
+
+    private static ParsingPackage pkg(String packageName, String... queriesPackages) {
+        ParsingPackage pkg = pkg(packageName);
+        if (queriesPackages != null) {
+            for (String queryPackageName : queriesPackages) {
+                pkg.addQueriesPackage(queryPackageName);
+            }
+        }
+        return pkg;
+    }
+
+    private static ParsingPackage pkg(String packageName, IntentFilter... filters) {
+        ParsedActivity activity = new ParsedActivity();
+        activity.setPackageName(packageName);
+        for (IntentFilter filter : filters) {
+            final ParsedActivityIntentInfo info = new ParsedActivityIntentInfo(packageName, null);
+            if (filter.countActions() > 0) {
+                filter.actionsIterator().forEachRemaining(info::addAction);
+            }
+            if (filter.countCategories() > 0) {
+                filter.actionsIterator().forEachRemaining(info::addAction);
+            }
+            if (filter.countDataAuthorities() > 0) {
+                filter.authoritiesIterator().forEachRemaining(info::addDataAuthority);
+            }
+            if (filter.countDataSchemes() > 0) {
+                filter.schemesIterator().forEachRemaining(info::addDataScheme);
+            }
+            activity.addIntent(info);
+        }
+
+        return pkg(packageName)
+                .addActivity(activity);
     }
 
     @Before
@@ -106,7 +119,7 @@
                 .checkPermission(anyString(), anyString(), anyInt()))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
         when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
-        when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+        when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
                 .thenReturn(true);
     }
 
@@ -152,7 +165,7 @@
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
         PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package",
-                new Intent("TEST_ACTION")).setApplicationInfoTargetSdkVersion(
+                new Intent("TEST_ACTION")).setTargetSdkVersion(
                 Build.VERSION_CODES.P)).build();
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -245,7 +258,7 @@
 
     @Test
     public void testNoQueries_FeatureOff_DoesntFilter() {
-        when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+        when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
                 .thenReturn(false);
         final AppsFilter appsFilter =
                 new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
@@ -301,13 +314,15 @@
     }
 
     private PackageSettingBuilder simulateAddPackage(AppsFilter filter,
-            PackageBuilder newPkgBuilder) {
-        PackageParser.Package newPkg = newPkgBuilder.build();
+            ParsingPackage newPkgBuilder) {
+        AndroidPackage newPkg = newPkgBuilder
+                .hideAsParsed()
+                .hideAsFinal();
         filter.addPackage(newPkg, mExisting);
-        mExisting.put(newPkg.packageName, newPkg);
+        mExisting.put(newPkg.getPackageName(), newPkg);
         return new PackageSettingBuilder()
                 .setPackage(newPkg)
-                .setName(newPkg.packageName)
+                .setName(newPkg.getPackageName())
                 .setCodePath("/")
                 .setResourcePath("/")
                 .setPVersionCode(1L);
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index fec3267..0273a1c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -19,17 +19,17 @@
 
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.test.AndroidTestCase;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LongSparseArray;
+
 import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
 import java.io.IOException;
-import java.security.cert.CertificateException;
 import java.security.PublicKey;
-
-import android.test.AndroidTestCase;
+import java.security.cert.CertificateException;
 
 public class KeySetManagerServiceTest extends AndroidTestCase {
 
@@ -39,7 +39,7 @@
     public PackageSetting generateFakePackageSetting(String name) {
         return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
                 new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
-                "", 1, 0, 0, null, null, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
+                "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
deleted file mode 100644
index c38672c..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageParser;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-
-class PackageBuilder {
-    final PackageParser.Package mPkg;
-
-    PackageBuilder(String packageName) {
-        mPkg = new PackageParser.Package(packageName);
-    }
-
-    PackageBuilder setApplicationInfoCodePath(String codePath) {
-        mPkg.applicationInfo.setCodePath(codePath);
-        return this;
-    }
-
-    PackageBuilder setApplicationInfoResourcePath(String resourcePath) {
-        mPkg.applicationInfo.setResourcePath(resourcePath);
-        return this;
-    }
-
-    PackageBuilder setCodePath(String codePath) {
-        mPkg.codePath = codePath;
-        return this;
-    }
-
-    PackageBuilder setBaseCodePath(String baseCodePath) {
-        mPkg.baseCodePath = baseCodePath;
-        return this;
-    }
-
-    PackageBuilder addUsesStaticLibrary(String name, long version) {
-        mPkg.usesStaticLibraries = ArrayUtils.add(mPkg.usesStaticLibraries, name);
-        mPkg.usesStaticLibrariesVersions =
-                ArrayUtils.appendLong(mPkg.usesStaticLibrariesVersions, version);
-        return this;
-    }
-
-    PackageBuilder setApplicationInfoNativeLibraryRootDir(String dir) {
-        mPkg.applicationInfo.nativeLibraryRootDir = dir;
-        return this;
-    }
-
-    PackageBuilder setStaticSharedLib(String staticSharedLibName, long staticSharedLibVersion) {
-        mPkg.staticSharedLibVersion = staticSharedLibVersion;
-        mPkg.staticSharedLibName = staticSharedLibName;
-        return this;
-    }
-
-    PackageBuilder setManifestPackageName(String manifestPackageName) {
-        mPkg.manifestPackageName = manifestPackageName;
-        return this;
-    }
-
-    PackageBuilder setVersionCodeMajor(int versionCodeMajor) {
-        mPkg.mVersionCodeMajor = versionCodeMajor;
-        return this;
-    }
-
-    PackageBuilder setVersionCode(int versionCode) {
-        mPkg.mVersionCode = versionCode;
-        return this;
-    }
-
-    PackageBuilder addSplitCodePath(String splitCodePath) {
-        mPkg.splitCodePaths =
-                ArrayUtils.appendElement(String.class, mPkg.splitCodePaths, splitCodePath);
-        return this;
-    }
-
-    PackageBuilder setApplicationInfoVolumeUuid(String volumeUuid) {
-        mPkg.applicationInfo.volumeUuid = volumeUuid;
-        return this;
-    }
-
-    PackageBuilder addLibraryName(String libraryName) {
-        mPkg.libraryNames = ArrayUtils.add(mPkg.libraryNames, libraryName);
-        return this;
-    }
-
-    PackageBuilder setRealPackageName(String realPackageName) {
-        mPkg.mRealPackage = realPackageName;
-        return this;
-    }
-
-    PackageBuilder setCpuAbiOVerride(String cpuAbiOverride) {
-        mPkg.cpuAbiOverride = cpuAbiOverride;
-        return this;
-    }
-
-    PackageBuilder addPermissionRequest(String permissionName) {
-        mPkg.requestedPermissions.add(permissionName);
-        return this;
-    }
-
-    PackageParser.Package build() {
-        return mPkg;
-    }
-
-    public PackageBuilder addApplicationInfoFlag(int flag) {
-        mPkg.applicationInfo.flags |= flag;
-        return this;
-    }
-
-    public PackageBuilder setApplicationInfoTargetSdkVersion(int versionCode) {
-        mPkg.applicationInfo.targetSdkVersion = versionCode;
-        return this;
-    }
-
-    public PackageBuilder setQueriesIntents(Collection<Intent> queriesIntents) {
-        mPkg.mQueriesIntents = new ArrayList<>(queriesIntents);
-        return this;
-    }
-
-    public PackageBuilder setQueriesIntents(Intent... intents) {
-        return setQueriesIntents(Arrays.asList(intents));
-    }
-
-    public PackageBuilder setQueriesPackages(Collection<String> queriesPackages) {
-        mPkg.mQueriesPackages = new ArrayList<>(queriesPackages);
-        return this;
-    }
-
-    public PackageBuilder setQueriesPackages(String... queriesPackages) {
-        return setQueriesPackages(Arrays.asList(queriesPackages));
-    }
-
-    public PackageBuilder setForceQueryable(boolean forceQueryable) {
-        mPkg.mForceQueryable = forceQueryable;
-        return this;
-    }
-
-    public interface ParseComponentArgsCreator {
-        PackageParser.ParseComponentArgs create(PackageParser.Package pkg);
-    }
-
-    public PackageBuilder addActivity(ParseComponentArgsCreator argsCreator, ActivityInfo info) {
-        mPkg.activities.add(new PackageParser.Activity(argsCreator.create(mPkg), info));
-        return this;
-    }
-
-    public interface ActivityIntentInfoCreator {
-        PackageParser.ActivityIntentInfo create(PackageParser.Activity activity);
-    }
-
-    public PackageBuilder addActivityIntentInfo(
-            int activityIndex, ActivityIntentInfoCreator creator) {
-        final PackageParser.Activity activity = mPkg.activities.get(activityIndex);
-        activity.intents.add(creator.create(activity));
-        return this;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 0a310d1..85840e1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -87,7 +87,7 @@
         setting = new PackageSetting("name", "realName", new File("codePath"),
                 new File("resourcePath"), "legacyNativeLibraryPathString",
                 "primaryCpuAbiString", "secondaryCpuAbiString",
-                "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0,
+                "cpuAbiOverrideString", 0, 0, 0, 0,
                 null, null);
         pri.populateUsers(new int[] {
                 1, 2, 3, 4, 5
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 3fe9b52..6836d3b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -346,10 +346,6 @@
 
     private static final String PACKAGE_NAME = "com.android.bar";
     private static final String REAL_PACKAGE_NAME = "com.android.foo";
-    private static final String PARENT_PACKAGE_NAME = "com.android.bar.parent";
-    private static final String CHILD_PACKAGE_NAME_01 = "com.android.bar.child01";
-    private static final String CHILD_PACKAGE_NAME_02 = "com.android.bar.child02";
-    private static final String CHILD_PACKAGE_NAME_03 = "com.android.bar.child03";
     private static final File INITIAL_CODE_PATH =
             new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1");
     private static final File UPDATED_CODE_PATH =
@@ -359,10 +355,6 @@
 
     @Test
     public void testPackageStateCopy01() {
-        final List<String> childPackageNames = new ArrayList<>();
-        childPackageNames.add(CHILD_PACKAGE_NAME_01);
-        childPackageNames.add(CHILD_PACKAGE_NAME_02);
-        childPackageNames.add(CHILD_PACKAGE_NAME_03);
         final PackageSetting origPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
@@ -375,8 +367,6 @@
                 INITIAL_VERSION_CODE,
                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
-                PARENT_PACKAGE_NAME,
-                childPackageNames,
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -386,10 +376,6 @@
 
     @Test
     public void testPackageStateCopy02() {
-        final List<String> childPackageNames = new ArrayList<>();
-        childPackageNames.add(CHILD_PACKAGE_NAME_01);
-        childPackageNames.add(CHILD_PACKAGE_NAME_02);
-        childPackageNames.add(CHILD_PACKAGE_NAME_03);
         final PackageSetting origPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
@@ -402,8 +388,6 @@
                 INITIAL_VERSION_CODE,
                 ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
-                PARENT_PACKAGE_NAME,
-                childPackageNames,
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -419,8 +403,6 @@
                 UPDATED_VERSION_CODE,
                 0 /*pkgFlags*/,
                 0 /*pkgPrivateFlags*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -448,7 +430,6 @@
                 "armeabi" /*secondaryCpuAbi*/,
                 0 /*pkgFlags*/,
                 0 /*pkgPrivateFlags*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -482,7 +463,6 @@
                 "armeabi" /*secondaryCpuAbi*/,
                 ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
                 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -522,7 +502,6 @@
                     "armeabi" /*secondaryCpuAbi*/,
                     0 /*pkgFlags*/,
                     0 /*pkgPrivateFlags*/,
-                    null /*childPkgNames*/,
                     UserManagerService.getInstance(),
                     null /*usesStaticLibraries*/,
                     null /*usesStaticLibrariesVersions*/);
@@ -555,8 +534,6 @@
                 false /*allowInstall*/,
                 false /*instantApp*/,
                 false /*virtualPreload*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -596,8 +573,6 @@
                 true /*allowInstall*/,
                 false /*instantApp*/,
                 false /*virtualPreload*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -643,8 +618,6 @@
                 false /*allowInstall*/,
                 false /*instantApp*/,
                 false /*virtualPreload*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -687,8 +660,6 @@
                 false /*allowInstall*/,
                 false /*instantApp*/,
                 false /*virtualPreload*/,
-                null /*parentPkgName*/,
-                null /*childPkgNames*/,
                 UserManagerService.getInstance(),
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -745,9 +716,6 @@
     private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
         assertThat(origPkgSetting, is(not(testPkgSetting)));
         assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
-        // different but equal objects
-        assertNotSame(origPkgSetting.childPackageNames, testPkgSetting.childPackageNames);
-        assertThat(origPkgSetting.childPackageNames, is(testPkgSetting.childPackageNames));
         assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
         assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
         assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
@@ -773,8 +741,6 @@
         // mOldCodePaths is _not_ copied
         // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
         // assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
-        assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
-        assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
         assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
         // No equals() method for this object
         // assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
@@ -826,8 +792,6 @@
                 INITIAL_VERSION_CODE,
                 pkgFlags,
                 0 /*privateFlags*/,
-                null /*parentPackageName*/,
-                null /*childPackageNames*/,
                 sharedUserId,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
@@ -846,8 +810,6 @@
                 INITIAL_VERSION_CODE,
                 0,
                 0 /*privateFlags*/,
-                null /*parentPackageName*/,
-                null /*childPackageNames*/,
                 0,
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index e33d8ca..162092b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,35 +15,49 @@
  */
 package com.android.server.pm;
 
-import static android.content.res.Resources.ID_NULL;
-
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.FeatureGroupInfo;
 import android.content.pm.FeatureInfo;
-import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
+import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
+import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
+import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
+import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackage;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.ArrayUtils;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -57,6 +71,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -64,6 +79,9 @@
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class PackageParserTest {
+    // TODO(b/135203078): Update this test with all fields and validate equality. Initial change
+    //  was just migrating to new interfaces. Consider adding actual equals() methods.
+
     @Rule
     public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
@@ -79,12 +97,12 @@
     @Test
     public void testParse_noCache() throws Exception {
         PackageParser pp = new CachePackageNameParser();
-        PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
+        ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
                 false /* useCaches */);
         assertNotNull(pkg);
 
         pp.setCacheDir(mTmpDir);
-        pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
+        pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
                 false /* useCaches */);
         assertNotNull(pkg);
 
@@ -99,27 +117,27 @@
 
         pp.setCacheDir(mTmpDir);
         // The first parse will write this package to the cache.
-        pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
+        pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
 
         // Now attempt to parse the package again, should return the
         // cached result.
-        PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
+        ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
                 true /* useCaches */);
-        assertEquals("cache_android", pkg.packageName);
+        assertEquals("cache_android", pkg.getPackageName());
 
         // Try again, with useCaches == false, shouldn't return the parsed
         // result.
-        pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
-        assertEquals("android", pkg.packageName);
+        pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
+        assertEquals("android", pkg.getPackageName());
 
         // We haven't set a cache directory here : the parse should still succeed,
         // just not using the cached results.
         pp = new CachePackageNameParser();
-        pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
-        assertEquals("android", pkg.packageName);
+        pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
+        assertEquals("android", pkg.getPackageName());
 
-        pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
-        assertEquals("android", pkg.packageName);
+        pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
+        assertEquals("android", pkg.getPackageName());
     }
 
     @Test
@@ -127,14 +145,14 @@
         PackageParser pp = new PackageParser();
         pp.setCacheDir(mTmpDir);
 
-        PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
-            true /* useCaches */);
+        ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
+                true /* useCaches */);
 
         Parcel p = Parcel.obtain();
         pkg.writeToParcel(p, 0 /* flags */);
 
         p.setDataPosition(0);
-        PackageParser.Package deserialized = new PackageParser.Package(p);
+        ParsedPackage deserialized = new PackageImpl(p);
 
         assertPackagesEqual(pkg, deserialized);
     }
@@ -143,154 +161,164 @@
     @SmallTest
     @Presubmit
     public void test_roundTripKnownFields() throws Exception {
-        PackageParser.Package pkg = new PackageParser.Package("foo");
+        ParsingPackage pkg = PackageImpl.forParsing("foo");
         setKnownFields(pkg);
 
         Parcel p = Parcel.obtain();
         pkg.writeToParcel(p, 0 /* flags */);
 
         p.setDataPosition(0);
-        PackageParser.Package deserialized = new PackageParser.Package(p);
+        ParsedPackage deserialized = new PackageImpl(p);
         assertAllFieldsExist(deserialized);
     }
 
     @Test
     public void test_stringInterning() throws Exception {
-        PackageParser.Package pkg = new PackageParser.Package("foo");
+        ParsingPackage pkg = PackageImpl.forParsing("foo");
         setKnownFields(pkg);
 
         Parcel p = Parcel.obtain();
         pkg.writeToParcel(p, 0 /* flags */);
 
         p.setDataPosition(0);
-        PackageParser.Package deserialized = new PackageParser.Package(p);
+        ParsingPackage deserialized = new PackageImpl(p);
 
         p.setDataPosition(0);
-        PackageParser.Package deserialized2 = new PackageParser.Package(p);
+        ParsingPackage deserialized2 = new PackageImpl(p);
 
-        assertSame(deserialized.packageName, deserialized2.packageName);
-        assertSame(deserialized.applicationInfo.permission,
-                deserialized2.applicationInfo.permission);
-        assertSame(deserialized.requestedPermissions.get(0),
-                deserialized2.requestedPermissions.get(0));
-        assertSame(deserialized.protectedBroadcasts.get(0),
-                deserialized2.protectedBroadcasts.get(0));
-        assertSame(deserialized.usesLibraries.get(0),
-                deserialized2.usesLibraries.get(0));
-        assertSame(deserialized.usesOptionalLibraries.get(0),
-                deserialized2.usesOptionalLibraries.get(0));
-        assertSame(deserialized.mVersionName, deserialized2.mVersionName);
-        assertSame(deserialized.mSharedUserId, deserialized2.mSharedUserId);
+        assertSame(deserialized.getPackageName(), deserialized2.getPackageName());
+        assertSame(deserialized.getPermission(),
+                deserialized2.getPermission());
+        assertSame(deserialized.getRequestedPermissions().get(0),
+                deserialized2.getRequestedPermissions().get(0));
+
+        List<String> protectedBroadcastsOne = new ArrayList<>(1);
+        protectedBroadcastsOne.addAll(deserialized.getProtectedBroadcasts());
+
+        List<String> protectedBroadcastsTwo = new ArrayList<>(1);
+        protectedBroadcastsTwo.addAll(deserialized2.getProtectedBroadcasts());
+
+        assertSame(protectedBroadcastsOne.get(0), protectedBroadcastsTwo.get(0));
+
+        assertSame(deserialized.getUsesLibraries().get(0),
+                deserialized2.getUsesLibraries().get(0));
+        assertSame(deserialized.getUsesOptionalLibraries().get(0),
+                deserialized2.getUsesOptionalLibraries().get(0));
+        assertSame(deserialized.getVersionName(), deserialized2.getVersionName());
+        assertSame(deserialized.getSharedUserId(), deserialized2.getSharedUserId());
     }
 
-
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
      */
     public static class CachePackageNameParser extends PackageParser {
         @Override
-        public byte[] toCacheEntry(Package pkg) {
-            return ("cache_" + pkg.packageName).getBytes(StandardCharsets.UTF_8);
+        public byte[] toCacheEntry(ParsedPackage pkg) {
+            return ("cache_" + pkg.getPackageName()).getBytes(StandardCharsets.UTF_8);
         }
 
         @Override
-        public Package fromCacheEntry(byte[] cacheEntry) {
-            return new Package(new String(cacheEntry, StandardCharsets.UTF_8));
+        public ParsedPackage fromCacheEntry(byte[] cacheEntry) {
+            return PackageImpl.forParsing(new String(cacheEntry, StandardCharsets.UTF_8))
+                    .hideAsParsed();
         }
     }
 
     // NOTE: The equality assertions below are based on code autogenerated by IntelliJ.
 
-    public static void assertPackagesEqual(PackageParser.Package a, PackageParser.Package b) {
-        assertEquals(a.baseRevisionCode, b.baseRevisionCode);
-        assertEquals(a.baseHardwareAccelerated, b.baseHardwareAccelerated);
-        assertEquals(a.mVersionCode, b.mVersionCode);
-        assertEquals(a.mSharedUserLabel, b.mSharedUserLabel);
-        assertEquals(a.mPreferredOrder, b.mPreferredOrder);
-        assertEquals(a.installLocation, b.installLocation);
-        assertEquals(a.coreApp, b.coreApp);
-        assertEquals(a.mRequiredForAllUsers, b.mRequiredForAllUsers);
-        assertEquals(a.mCompileSdkVersion, b.mCompileSdkVersion);
-        assertEquals(a.mCompileSdkVersionCodename, b.mCompileSdkVersionCodename);
-        assertEquals(a.use32bitAbi, b.use32bitAbi);
-        assertEquals(a.packageName, b.packageName);
-        assertTrue(Arrays.equals(a.splitNames, b.splitNames));
-        assertEquals(a.volumeUuid, b.volumeUuid);
-        assertEquals(a.codePath, b.codePath);
-        assertEquals(a.baseCodePath, b.baseCodePath);
-        assertTrue(Arrays.equals(a.splitCodePaths, b.splitCodePaths));
-        assertTrue(Arrays.equals(a.splitRevisionCodes, b.splitRevisionCodes));
-        assertTrue(Arrays.equals(a.splitFlags, b.splitFlags));
-        assertTrue(Arrays.equals(a.splitPrivateFlags, b.splitPrivateFlags));
-        assertApplicationInfoEqual(a.applicationInfo, b.applicationInfo);
+    public static void assertPackagesEqual(AndroidPackage a, AndroidPackage b) {
+        assertEquals(a.getBaseRevisionCode(), b.getBaseRevisionCode());
+        assertEquals(a.isBaseHardwareAccelerated(), b.isBaseHardwareAccelerated());
+        assertEquals(a.getVersionCode(), b.getVersionCode());
+        assertEquals(a.getSharedUserLabel(), b.getSharedUserLabel());
+        assertEquals(a.getPreferredOrder(), b.getPreferredOrder());
+        assertEquals(a.getInstallLocation(), b.getInstallLocation());
+        assertEquals(a.isCoreApp(), b.isCoreApp());
+        assertEquals(a.isRequiredForAllUsers(), b.isRequiredForAllUsers());
+        assertEquals(a.getCompileSdkVersion(), b.getCompileSdkVersion());
+        assertEquals(a.getCompileSdkVersionCodeName(), b.getCompileSdkVersionCodeName());
+        assertEquals(a.isUse32BitAbi(), b.isUse32BitAbi());
+        assertEquals(a.getPackageName(), b.getPackageName());
+        assertArrayEquals(a.getSplitNames(), b.getSplitNames());
+        assertEquals(a.getVolumeUuid(), b.getVolumeUuid());
+        assertEquals(a.getCodePath(), b.getCodePath());
+        assertEquals(a.getBaseCodePath(), b.getBaseCodePath());
+        assertArrayEquals(a.getSplitCodePaths(), b.getSplitCodePaths());
+        assertArrayEquals(a.getSplitRevisionCodes(), b.getSplitRevisionCodes());
+        assertArrayEquals(a.getSplitFlags(), b.getSplitFlags());
 
-        assertEquals(a.permissions.size(), b.permissions.size());
-        for (int i = 0; i < a.permissions.size(); ++i) {
-            assertPermissionsEqual(a.permissions.get(i), b.permissions.get(i));
-            assertSame(a.permissions.get(i).owner, a);
-            assertSame(b.permissions.get(i).owner, b);
+        PackageInfo aInfo = PackageInfoUtils.generate(a, new int[]{}, 0, 0, 0,
+                Collections.emptySet(), new PackageUserState(), 0);
+        PackageInfo bInfo = PackageInfoUtils.generate(b, new int[]{}, 0, 0, 0,
+                Collections.emptySet(), new PackageUserState(), 0);
+        assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
+
+        assertEquals(ArrayUtils.size(a.getPermissions()), ArrayUtils.size(b.getPermissions()));
+        for (int i = 0; i < ArrayUtils.size(a.getPermissions()); ++i) {
+            assertPermissionsEqual(a.getPermissions().get(i), b.getPermissions().get(i));
         }
 
-        assertEquals(a.permissionGroups.size(), b.permissionGroups.size());
-        for (int i = 0; i < a.permissionGroups.size(); ++i) {
-            assertPermissionGroupsEqual(a.permissionGroups.get(i), b.permissionGroups.get(i));
+        assertEquals(ArrayUtils.size(a.getPermissionGroups()),
+                ArrayUtils.size(b.getPermissionGroups()));
+        for (int i = 0; i < a.getPermissionGroups().size(); ++i) {
+            assertPermissionGroupsEqual(a.getPermissionGroups().get(i),
+                    b.getPermissionGroups().get(i));
         }
 
-        assertEquals(a.activities.size(), b.activities.size());
-        for (int i = 0; i < a.activities.size(); ++i) {
-            assertActivitiesEqual(a.activities.get(i), b.activities.get(i));
+        assertEquals(ArrayUtils.size(a.getActivities()), ArrayUtils.size(b.getActivities()));
+        for (int i = 0; i < ArrayUtils.size(a.getActivities()); ++i) {
+            assertActivitiesEqual(a, a.getActivities().get(i), b, b.getActivities().get(i));
         }
 
-        assertEquals(a.receivers.size(), b.receivers.size());
-        for (int i = 0; i < a.receivers.size(); ++i) {
-            assertActivitiesEqual(a.receivers.get(i), b.receivers.get(i));
+        assertEquals(ArrayUtils.size(a.getReceivers()), ArrayUtils.size(b.getReceivers()));
+        for (int i = 0; i < ArrayUtils.size(a.getReceivers()); ++i) {
+            assertActivitiesEqual(a, a.getReceivers().get(i), b, b.getReceivers().get(i));
         }
 
-        assertEquals(a.providers.size(), b.providers.size());
-        for (int i = 0; i < a.providers.size(); ++i) {
-            assertProvidersEqual(a.providers.get(i), b.providers.get(i));
+        assertEquals(ArrayUtils.size(a.getProviders()), ArrayUtils.size(b.getProviders()));
+        for (int i = 0; i < ArrayUtils.size(a.getProviders()); ++i) {
+            assertProvidersEqual(a, a.getProviders().get(i), b, b.getProviders().get(i));
         }
 
-        assertEquals(a.services.size(), b.services.size());
-        for (int i = 0; i < a.services.size(); ++i) {
-            assertServicesEqual(a.services.get(i), b.services.get(i));
+        assertEquals(ArrayUtils.size(a.getServices()), ArrayUtils.size(b.getServices()));
+        for (int i = 0; i < ArrayUtils.size(a.getServices()); ++i) {
+            assertServicesEqual(a, a.getServices().get(i), b, b.getServices().get(i));
         }
 
-        assertEquals(a.instrumentation.size(), b.instrumentation.size());
-        for (int i = 0; i < a.instrumentation.size(); ++i) {
-            assertInstrumentationEqual(a.instrumentation.get(i), b.instrumentation.get(i));
+        assertEquals(ArrayUtils.size(a.getInstrumentations()),
+                ArrayUtils.size(b.getInstrumentations()));
+        for (int i = 0; i < ArrayUtils.size(a.getInstrumentations()); ++i) {
+            assertInstrumentationEqual(a.getInstrumentations().get(i),
+                    b.getInstrumentations().get(i));
         }
 
-        assertEquals(a.requestedPermissions, b.requestedPermissions);
-        assertEquals(a.protectedBroadcasts, b.protectedBroadcasts);
-        assertEquals(a.parentPackage, b.parentPackage);
-        assertEquals(a.childPackages, b.childPackages);
-        assertEquals(a.libraryNames, b.libraryNames);
-        assertEquals(a.usesLibraries, b.usesLibraries);
-        assertEquals(a.usesOptionalLibraries, b.usesOptionalLibraries);
-        assertTrue(Arrays.equals(a.usesLibraryFiles, b.usesLibraryFiles));
-        assertEquals(a.mOriginalPackages, b.mOriginalPackages);
-        assertEquals(a.mRealPackage, b.mRealPackage);
-        assertEquals(a.mAdoptPermissions, b.mAdoptPermissions);
-        assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData);
-        assertEquals(a.mVersionName, b.mVersionName);
-        assertEquals(a.mSharedUserId, b.mSharedUserId);
-        assertTrue(Arrays.equals(a.mSigningDetails.signatures, b.mSigningDetails.signatures));
-        assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills));
-        assertEquals(a.mExtras, b.mExtras);
-        assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType);
-        assertEquals(a.mRequiredAccountType, b.mRequiredAccountType);
-        assertEquals(a.mOverlayTarget, b.mOverlayTarget);
-        assertEquals(a.mOverlayTargetName, b.mOverlayTargetName);
-        assertEquals(a.mOverlayCategory, b.mOverlayCategory);
-        assertEquals(a.mOverlayPriority, b.mOverlayPriority);
-        assertEquals(a.mOverlayIsStatic, b.mOverlayIsStatic);
-        assertEquals(a.mSigningDetails.publicKeys, b.mSigningDetails.publicKeys);
-        assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets);
-        assertEquals(a.mKeySetMapping, b.mKeySetMapping);
-        assertEquals(a.cpuAbiOverride, b.cpuAbiOverride);
-        assertTrue(Arrays.equals(a.restrictUpdateHash, b.restrictUpdateHash));
+        assertEquals(a.getRequestedPermissions(), b.getRequestedPermissions());
+        assertEquals(a.getProtectedBroadcasts(), b.getProtectedBroadcasts());
+        assertEquals(a.getLibraryNames(), b.getLibraryNames());
+        assertEquals(a.getUsesLibraries(), b.getUsesLibraries());
+        assertEquals(a.getUsesOptionalLibraries(), b.getUsesOptionalLibraries());
+        assertArrayEquals(a.getUsesLibraryFiles(), b.getUsesLibraryFiles());
+        assertEquals(a.getOriginalPackages(), b.getOriginalPackages());
+        assertEquals(a.getRealPackage(), b.getRealPackage());
+        assertEquals(a.getAdoptPermissions(), b.getAdoptPermissions());
+        assertBundleApproximateEquals(a.getAppMetaData(), b.getAppMetaData());
+        assertEquals(a.getVersionName(), b.getVersionName());
+        assertEquals(a.getSharedUserId(), b.getSharedUserId());
+        assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
+        assertArrayEquals(a.getLastPackageUsageTimeInMills(), b.getLastPackageUsageTimeInMills());
+        assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
+        assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
+        assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
+        assertEquals(a.getOverlayTargetName(), b.getOverlayTargetName());
+        assertEquals(a.getOverlayCategory(), b.getOverlayCategory());
+        assertEquals(a.getOverlayPriority(), b.getOverlayPriority());
+        assertEquals(a.isOverlayIsStatic(), b.isOverlayIsStatic());
+        assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
+        assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
+        assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
+        assertEquals(a.getCpuAbiOverride(), b.getCpuAbiOverride());
+        assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
     }
 
     private static void assertBundleApproximateEquals(Bundle a, Bundle b) {
@@ -305,10 +333,10 @@
         assertEquals(a.toString(), b.toString());
     }
 
-    private static void assertComponentsEqual(PackageParser.Component<?> a,
-                                              PackageParser.Component<?> b) {
+    private static void assertComponentsEqual(ParsedComponent<?> a,
+            ParsedComponent<?> b) {
         assertEquals(a.className, b.className);
-        assertBundleApproximateEquals(a.metaData, b.metaData);
+        assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
         assertEquals(a.getComponentName(), b.getComponentName());
 
         if (a.intents != null && b.intents != null) {
@@ -318,80 +346,104 @@
         }
 
         for (int i = 0; i < a.intents.size(); ++i) {
-            PackageParser.IntentInfo aIntent = a.intents.get(i);
-            PackageParser.IntentInfo bIntent = b.intents.get(i);
+            ParsedIntentInfo aIntent = a.intents.get(i);
+            ParsedIntentInfo bIntent = b.intents.get(i);
 
             assertEquals(aIntent.hasDefault, bIntent.hasDefault);
             assertEquals(aIntent.labelRes, bIntent.labelRes);
             assertEquals(aIntent.nonLocalizedLabel, bIntent.nonLocalizedLabel);
             assertEquals(aIntent.icon, bIntent.icon);
-            assertEquals(aIntent.logo, bIntent.logo);
-            assertEquals(aIntent.banner, bIntent.banner);
-            assertEquals(aIntent.preferred, bIntent.preferred);
         }
     }
 
-    private static void assertPermissionsEqual(PackageParser.Permission a,
-                                               PackageParser.Permission b) {
+    private static void assertPermissionsEqual(ParsedPermission a,
+            ParsedPermission b) {
         assertComponentsEqual(a, b);
         assertEquals(a.tree, b.tree);
 
         // Verify basic flags in PermissionInfo to make sure they're consistent. We don't perform
         // a full structural equality here because the code that serializes them isn't parser
         // specific and is tested elsewhere.
-        assertEquals(a.info.protectionLevel, b.info.protectionLevel);
-        assertEquals(a.info.group, b.info.group);
-        assertEquals(a.info.flags, b.info.flags);
+        assertEquals(a.getProtection(), b.getProtection());
+        assertEquals(a.getGroup(), b.getGroup());
+        assertEquals(a.flags, b.flags);
 
-        if (a.group != null && b.group != null) {
-            assertPermissionGroupsEqual(a.group, b.group);
-        } else if (a.group != null || b.group != null) {
+        if (a.parsedPermissionGroup != null && b.parsedPermissionGroup != null) {
+            assertPermissionGroupsEqual(a.parsedPermissionGroup, b.parsedPermissionGroup);
+        } else if (a.parsedPermissionGroup != null || b.parsedPermissionGroup != null) {
             throw new AssertionError();
         }
     }
 
-    private static void assertInstrumentationEqual(PackageParser.Instrumentation a,
-                                                   PackageParser.Instrumentation b) {
+    private static void assertInstrumentationEqual(ParsedInstrumentation a,
+            ParsedInstrumentation b) {
         assertComponentsEqual(a, b);
 
         // Sanity check for InstrumentationInfo.
-        assertEquals(a.info.targetPackage, b.info.targetPackage);
-        assertEquals(a.info.targetProcesses, b.info.targetProcesses);
-        assertEquals(a.info.sourceDir, b.info.sourceDir);
-        assertEquals(a.info.publicSourceDir, b.info.publicSourceDir);
+        assertEquals(a.getTargetPackage(), b.getTargetPackage());
+        assertEquals(a.getTargetProcesses(), b.getTargetProcesses());
+        assertEquals(a.sourceDir, b.sourceDir);
+        assertEquals(a.publicSourceDir, b.publicSourceDir);
     }
 
-    private static void assertServicesEqual(PackageParser.Service a, PackageParser.Service b) {
+    private static void assertServicesEqual(
+            AndroidPackage aPkg,
+            ParsedService a,
+            AndroidPackage bPkg,
+            ParsedService b
+    ) {
         assertComponentsEqual(a, b);
 
         // Sanity check for ServiceInfo.
-        assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo);
-        assertEquals(a.info.name, b.info.name);
+        ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0, new PackageUserState(),
+                0);
+        ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0, new PackageUserState(),
+                0);
+        assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
+        assertEquals(a.getName(), b.getName());
     }
 
-    private static void assertProvidersEqual(PackageParser.Provider a, PackageParser.Provider b) {
+    private static void assertProvidersEqual(
+            AndroidPackage aPkg,
+            ParsedProvider a,
+            AndroidPackage bPkg,
+            ParsedProvider b
+    ) {
         assertComponentsEqual(a, b);
 
         // Sanity check for ProviderInfo
-        assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo);
-        assertEquals(a.info.name, b.info.name);
+        ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0,
+                new PackageUserState(), 0);
+        ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0,
+                new PackageUserState(), 0);
+        assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
+        assertEquals(a.getName(), b.getName());
     }
 
-    private static void assertActivitiesEqual(PackageParser.Activity a, PackageParser.Activity b) {
+    private static void assertActivitiesEqual(
+            AndroidPackage aPkg,
+            ParsedActivity a,
+            AndroidPackage bPkg,
+            ParsedActivity b
+    ) {
         assertComponentsEqual(a, b);
 
         // Sanity check for ActivityInfo.
-        assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo);
-        assertEquals(a.info.name, b.info.name);
+        ActivityInfo aInfo = PackageInfoUtils.generateActivityInfo(aPkg, a, 0,
+                new PackageUserState(), 0);
+        ActivityInfo bInfo = PackageInfoUtils.generateActivityInfo(bPkg, b, 0,
+                new PackageUserState(), 0);
+        assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
+        assertEquals(a.getName(), b.getName());
     }
 
-    private static void assertPermissionGroupsEqual(PackageParser.PermissionGroup a,
-                                                    PackageParser.PermissionGroup b) {
+    private static void assertPermissionGroupsEqual(ParsedPermissionGroup a,
+            ParsedPermissionGroup b) {
         assertComponentsEqual(a, b);
 
         // Sanity check for PermissionGroupInfo.
-        assertEquals(a.info.name, b.info.name);
-        assertEquals(a.info.descriptionRes, b.info.descriptionRes);
+        assertEquals(a.getName(), b.getName());
+        assertEquals(a.descriptionRes, b.descriptionRes);
     }
 
     private static void assertApplicationInfoEqual(ApplicationInfo a, ApplicationInfo that) {
@@ -424,11 +476,11 @@
         assertEquals(a.scanPublicSourceDir, that.scanPublicSourceDir);
         assertEquals(a.sourceDir, that.sourceDir);
         assertEquals(a.publicSourceDir, that.publicSourceDir);
-        assertTrue(Arrays.equals(a.splitSourceDirs, that.splitSourceDirs));
-        assertTrue(Arrays.equals(a.splitPublicSourceDirs, that.splitPublicSourceDirs));
-        assertTrue(Arrays.equals(a.resourceDirs, that.resourceDirs));
+        assertArrayEquals(a.splitSourceDirs, that.splitSourceDirs);
+        assertArrayEquals(a.splitPublicSourceDirs, that.splitPublicSourceDirs);
+        assertArrayEquals(a.resourceDirs, that.resourceDirs);
         assertEquals(a.seInfo, that.seInfo);
-        assertTrue(Arrays.equals(a.sharedLibraryFiles, that.sharedLibraryFiles));
+        assertArrayEquals(a.sharedLibraryFiles, that.sharedLibraryFiles);
         assertEquals(a.dataDir, that.dataDir);
         assertEquals(a.deviceProtectedDataDir, that.deviceProtectedDataDir);
         assertEquals(a.credentialProtectedDataDir, that.credentialProtectedDataDir);
@@ -439,132 +491,93 @@
         assertEquals(a.secondaryCpuAbi, that.secondaryCpuAbi);
     }
 
-    public static void setKnownFields(PackageParser.Package pkg) {
-        pkg.baseRevisionCode = 100;
-        pkg.baseHardwareAccelerated = true;
-        pkg.mVersionCode = 100;
-        pkg.mSharedUserLabel = 100;
-        pkg.mPreferredOrder = 100;
-        pkg.installLocation = 100;
-        pkg.coreApp = true;
-        pkg.mRequiredForAllUsers = true;
-        pkg.use32bitAbi = true;
-        pkg.packageName = "foo";
-        pkg.splitNames = new String[] { "foo2" };
-        pkg.volumeUuid = "foo3";
-        pkg.codePath = "foo4";
-        pkg.baseCodePath = "foo5";
-        pkg.splitCodePaths = new String[] { "foo6" };
-        pkg.splitRevisionCodes = new int[] { 100 };
-        pkg.splitFlags = new int[] { 100 };
-        pkg.splitPrivateFlags = new int[] { 100 };
-        pkg.applicationInfo = new ApplicationInfo();
+    public static void setKnownFields(ParsingPackage pkg) {
+        Bundle bundle = new Bundle();
+        bundle.putString("key", "value");
 
-        pkg.permissions.add(new PackageParser.Permission(pkg, (String) null));
-        pkg.permissionGroups.add(new PackageParser.PermissionGroup(pkg, ID_NULL, ID_NULL, ID_NULL));
+        ParsedPermission permission = new ParsedPermission();
+        permission.parsedPermissionGroup = new ParsedPermissionGroup();
 
-        final PackageParser.ParseComponentArgs dummy = new PackageParser.ParseComponentArgs(
-                pkg, new String[1], 0, 0, 0, 0, 0, 0, null, 0, 0, 0);
-
-        pkg.activities.add(new PackageParser.Activity(dummy, new ActivityInfo()));
-        pkg.receivers.add(new PackageParser.Activity(dummy, new ActivityInfo()));
-        pkg.providers.add(new PackageParser.Provider(dummy, new ProviderInfo()));
-        pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
-        pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
-        pkg.requestedPermissions.add("foo7");
-        pkg.implicitPermissions.add("foo25");
-
-        pkg.protectedBroadcasts = new ArrayList<>();
-        pkg.protectedBroadcasts.add("foo8");
-
-        pkg.parentPackage = new PackageParser.Package("foo9");
-
-        pkg.childPackages = new ArrayList<>();
-        pkg.childPackages.add(new PackageParser.Package("bar"));
-
-        pkg.staticSharedLibName = "foo23";
-        pkg.staticSharedLibVersion = 100;
-        pkg.usesStaticLibraries = new ArrayList<>();
-        pkg.usesStaticLibraries.add("foo23");
-        pkg.usesStaticLibrariesCertDigests = new String[1][];
-        pkg.usesStaticLibrariesCertDigests[0] = new String[] { "digest" };
-        pkg.usesStaticLibrariesVersions = new long[] { 100 };
-
-        pkg.libraryNames = new ArrayList<>();
-        pkg.libraryNames.add("foo10");
-
-        pkg.usesLibraries = new ArrayList<>();
-        pkg.usesLibraries.add("foo11");
-
-        pkg.usesOptionalLibraries = new ArrayList<>();
-        pkg.usesOptionalLibraries.add("foo12");
-
-        pkg.usesLibraryFiles = new String[] { "foo13"};
-
-        pkg.usesLibraryInfos = new ArrayList<>();
-        pkg.usesLibraryInfos.add(
-                new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null));
-
-        pkg.mOriginalPackages = new ArrayList<>();
-        pkg.mOriginalPackages.add("foo14");
-
-        pkg.mRealPackage = "foo15";
-
-        pkg.mAdoptPermissions = new ArrayList<>();
-        pkg.mAdoptPermissions.add("foo16");
-
-        pkg.mAppMetaData = new Bundle();
-        pkg.mVersionName = "foo17";
-        pkg.mSharedUserId = "foo18";
-        pkg.mSigningDetails =
-                new PackageParser.SigningDetails(
-                        new Signature[] { new Signature(new byte[16]) },
-                        2,
-                        new ArraySet<>(),
-                        null);
-        pkg.mExtras = new Bundle();
-        pkg.mRestrictedAccountType = "foo19";
-        pkg.mRequiredAccountType = "foo20";
-        pkg.mOverlayTarget = "foo21";
-        pkg.mOverlayPriority = 100;
-        pkg.mUpgradeKeySets = new ArraySet<>();
-        pkg.mKeySetMapping = new ArrayMap<>();
-        pkg.cpuAbiOverride = "foo22";
-        pkg.restrictUpdateHash = new byte[16];
-
-        pkg.preferredActivityFilters = new ArrayList<>();
-        pkg.preferredActivityFilters.add(new PackageParser.ActivityIntentInfo(
-                new PackageParser.Activity(dummy, new ActivityInfo())));
-
-        pkg.configPreferences = new ArrayList<>();
-        pkg.configPreferences.add(new ConfigurationInfo());
-
-        pkg.reqFeatures = new ArrayList<>();
-        pkg.reqFeatures.add(new FeatureInfo());
-
-        pkg.featureGroups = new ArrayList<>();
-        pkg.featureGroups.add(new FeatureGroupInfo());
-
-        pkg.mCompileSdkVersionCodename = "foo23";
-        pkg.mCompileSdkVersion = 100;
-        pkg.mVersionCodeMajor = 100;
-
-        pkg.mOverlayCategory = "foo24";
-        pkg.mOverlayIsStatic = true;
-        pkg.mOverlayTargetName = "foo26";
-
-        pkg.baseHardwareAccelerated = true;
-        pkg.coreApp = true;
-        pkg.mRequiredForAllUsers = true;
-        pkg.visibleToInstantApps = true;
-        pkg.use32bitAbi = true;
-        pkg.mForceQueryable = true;
-        pkg.mQueriesPackages = new ArrayList<>(Arrays.asList("foo27"));
-        pkg.mQueriesIntents = new ArrayList<>(Arrays.asList(new Intent("foo28")));
+        pkg.setBaseRevisionCode(100)
+                .setBaseHardwareAccelerated(true)
+                .setSharedUserLabel(100)
+                .setPreferredOrder(100)
+                .setInstallLocation(100)
+                .setRequiredForAllUsers(true)
+                .asSplit(
+                        new String[]{"foo2"},
+                        new String[]{"foo6"},
+                        new int[]{100},
+                        null
+                )
+                .setUse32BitAbi(true)
+                .setVolumeUuid("foo3")
+                .setCodePath("foo4")
+                .addPermission(permission)
+                .addPermissionGroup(new ParsedPermissionGroup())
+                .addActivity(new ParsedActivity())
+                .addReceiver(new ParsedActivity())
+                .addProvider(new ParsedProvider())
+                .addService(new ParsedService())
+                .addInstrumentation(new ParsedInstrumentation())
+                .addRequestedPermission("foo7")
+                .addImplicitPermission("foo25")
+                .addProtectedBroadcast("foo8")
+                .setStaticSharedLibName("foo23")
+                .setStaticSharedLibVersion(100)
+                .addUsesStaticLibrary("foo23")
+                .addUsesStaticLibraryCertDigests(new String[]{"digest"})
+                .addUsesStaticLibraryVersion(100)
+                .addLibraryName("foo10")
+                .addUsesLibrary("foo11")
+                .addUsesOptionalLibrary("foo12")
+                .addOriginalPackage("foo14")
+                .setRealPackage("foo15")
+                .addAdoptPermission("foo16")
+                .setAppMetaData(bundle)
+                .setVersionName("foo17")
+                .setSharedUserId("foo18")
+                .setSigningDetails(
+                        new PackageParser.SigningDetails(
+                                new Signature[]{new Signature(new byte[16])},
+                                2,
+                                new ArraySet<>(),
+                                null)
+                )
+                .setRestrictedAccountType("foo19")
+                .setRequiredAccountType("foo20")
+                .setOverlayTarget("foo21")
+                .setOverlayPriority(100)
+                .setUpgradeKeySets(new ArraySet<>())
+                .addPreferredActivityFilter(
+                        new ComponentParseUtils.ParsedActivityIntentInfo("foo", "className"))
+                .addConfigPreference(new ConfigurationInfo())
+                .addReqFeature(new FeatureInfo())
+                .addFeatureGroup(new FeatureGroupInfo())
+                .setCompileSdkVersionCodename("foo23")
+                .setCompileSdkVersion(100)
+                .setOverlayCategory("foo24")
+                .setOverlayIsStatic(true)
+                .setOverlayTargetName("foo26")
+                .setVisibleToInstantApps(true)
+                .setSplitHasCode(0, true)
+                .hideAsParsed()
+                .setBaseCodePath("foo5")
+                .setVersionCode(100)
+                .setCpuAbiOverride("foo22")
+                .setRestrictUpdateHash(new byte[16])
+                .setVersionCodeMajor(100)
+                .setCoreApp(true)
+                .hideAsFinal()
+                .mutate()
+                .setUsesLibraryInfos(Arrays.asList(
+                        new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null)
+                ))
+                .setUsesLibraryFiles(new String[]{"foo13"});
     }
 
-    private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception {
-        Field[] fields = PackageParser.Package.class.getDeclaredFields();
+    private static void assertAllFieldsExist(ParsedPackage pkg) throws Exception {
+        Field[] fields = ParsedPackage.class.getDeclaredFields();
 
         Set<String> nonSerializedFields = new HashSet<>();
         nonSerializedFields.add("mExtras");
@@ -601,7 +614,7 @@
             } else if (fieldType == boolean.class) {
                 // boolean fields: Check that they're set to true.
                 boolean value = (boolean) f.get(pkg);
-                assertEquals("Bad value for field: " + f, true, value);
+                assertTrue("Bad value for field: " + f, value);
             } else {
                 // All other fields: Check that they're set.
                 Object o = f.get(pkg);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 06c6314..ca9e5b1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,12 +16,11 @@
 
 package com.android.server.pm;
 
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
+import android.content.pm.parsing.AndroidPackage;
 import android.util.SparseArray;
 
 import java.io.File;
-import java.util.List;
 
 class PackageSettingBuilder {
     private String mName;
@@ -35,16 +34,14 @@
     private long mPVersionCode;
     private int mPkgFlags;
     private int mPrivateFlags;
-    private String mParentPackageName;
-    private List<String> mChildPackageNames;
     private int mSharedUserId;
     private String[] mUsesStaticLibraries;
     private long[] mUsesStaticLibrariesVersions;
     private String mVolumeUuid;
     private SparseArray<PackageUserState> mUserStates = new SparseArray<>();
-    private PackageParser.Package mPkg;
+    private AndroidPackage mPkg;
 
-    public PackageSettingBuilder setPackage(PackageParser.Package pkg) {
+    public PackageSettingBuilder setPackage(AndroidPackage pkg) {
         this.mPkg = pkg;
         return this;
     }
@@ -105,16 +102,6 @@
         return this;
     }
 
-    public PackageSettingBuilder setParentPackageName(String parentPackageName) {
-        this.mParentPackageName = parentPackageName;
-        return this;
-    }
-
-    public PackageSettingBuilder setChildPackageNames(List<String> childPackageNames) {
-        this.mChildPackageNames = childPackageNames;
-        return this;
-    }
-
     public PackageSettingBuilder setSharedUserId(int sharedUserId) {
         this.mSharedUserId = sharedUserId;
         return this;
@@ -148,9 +135,8 @@
         final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
                 new File(mCodePath), new File(mResourcePath),
                 mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
-                mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mParentPackageName,
-                mChildPackageNames, mSharedUserId, mUsesStaticLibraries,
-                mUsesStaticLibrariesVersions);
+                mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId,
+                mUsesStaticLibraries, mUsesStaticLibrariesVersions);
         packageSetting.pkg = mPkg;
         packageSetting.volumeUuid = this.mVolumeUuid;
         for (int i = 0; i < mUserStates.size(); i++) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index d3a77d3..04e769d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -16,8 +16,8 @@
 package com.android.server.pm;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -467,8 +467,7 @@
         File appPath = new File("/data/app/app");
         PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
                 "/data/app/app", null, null, null,
-                1, 940097092, 0, null,
-                null, 0 /*userId*/, null, null);
+                1, 940097092, 0, 0 /*userId*/, null, null);
         return result;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
index 41489dc..a0efc8a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsedPackage;
 import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -74,7 +75,7 @@
         }
 
         @Override
-        protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
+        protected ParsedPackage parsePackage(PackageParser packageParser, File scanFile,
                 int parseFlags) throws PackageParser.PackageParserException {
             // Do not actually parse the package for testing
             return null;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
index 34a3f86..11f154b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -16,12 +16,13 @@
 
 package com.android.server.pm;
 
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsedPackage;
 import android.os.UserHandle;
 
 class ScanRequestBuilder {
-    private final PackageParser.Package mPkg;
-    private PackageParser.Package mOldPkg;
+    private final ParsedPackage mPkg;
+    private AndroidPackage mOldPkg;
     private SharedUserSetting mSharedUserSetting;
     private PackageSetting mPkgSetting;
     private PackageSetting mDisabledPkgSetting;
@@ -32,11 +33,11 @@
     private UserHandle mUser;
     private boolean mIsPlatformPackage;
 
-    ScanRequestBuilder(PackageParser.Package pkg) {
+    ScanRequestBuilder(ParsedPackage pkg) {
         this.mPkg = pkg;
     }
 
-    public ScanRequestBuilder setOldPkg(PackageParser.Package oldPkg) {
+    public ScanRequestBuilder setOldPkg(AndroidPackage oldPkg) {
         this.mOldPkg = oldPkg;
         return this;
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 05905d9..1f027a3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -35,13 +35,17 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.TypedArray;
 import android.os.Environment;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
@@ -58,6 +62,7 @@
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
+import java.util.UUID;
 
 @RunWith(MockitoJUnitRunner.class)
 @Presubmit
@@ -66,6 +71,9 @@
 
     private static final String DUMMY_PACKAGE_NAME = "some.app.to.test";
 
+    private static final UUID UUID_ONE = UUID.randomUUID();
+    private static final UUID UUID_TWO = UUID.randomUUID();
+
     @Mock
     PackageAbiHelper mMockPackageAbiHelper;
     @Mock
@@ -87,25 +95,25 @@
     @Before
     public void setupDefaultAbiBehavior() throws Exception {
         when(mMockPackageAbiHelper.derivePackageAbi(
-                any(PackageParser.Package.class), nullable(String.class), anyBoolean()))
+                any(ParsedPackage.class), nullable(String.class), anyBoolean()))
                 .thenReturn(new Pair<>(
                         new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
                         new PackageAbiHelper.NativeLibraryPaths(
                                 "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
         when(mMockPackageAbiHelper.getNativeLibraryPaths(
-                any(PackageParser.Package.class), any(File.class)))
+                any(ParsedPackage.class), any(File.class)))
                 .thenReturn(new PackageAbiHelper.NativeLibraryPaths(
                         "getRootDir", true, "getNativeDir", "getNativeDir2"
                 ));
         when(mMockPackageAbiHelper.getBundledAppAbis(
-                any(PackageParser.Package.class)))
+                any(ParsedPackage.class)))
                 .thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary"));
     }
 
     @Test
     public void newInstallSimpleAllNominal() throws Exception {
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
                         .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
                         .build();
@@ -123,7 +131,7 @@
         when(mMockUserManager.getUserIds()).thenReturn(userIds);
 
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setRealPkgName(null)
                         .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
                         .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
@@ -138,7 +146,7 @@
     @Test
     public void installRealPackageName() throws Exception {
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setRealPkgName("com.package.real")
                         .build();
 
@@ -149,7 +157,7 @@
         final PackageManagerService.ScanRequest scanRequestNoRealPkg =
                 createBasicScanRequestBuilder(
                         createBasicPackage(DUMMY_PACKAGE_NAME)
-                                .setRealPackageName("com.package.real").build())
+                                .setRealPackage("com.package.real"))
                         .build();
 
         final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
@@ -165,7 +173,7 @@
                 .setSecondaryCpuAbiString("secondaryCpuAbi")
                 .build();
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
                         .setPkgSetting(pkgSetting)
                         .build();
@@ -197,7 +205,7 @@
                         .build();
 
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setPkgSetting(existingPkgSetting)
                         .build();
 
@@ -209,17 +217,18 @@
 
     @Test
     public void installStaticSharedLibrary() throws Exception {
-        final PackageParser.Package pkg = createBasicPackage("static.lib.pkg.123")
-                .setStaticSharedLib("static.lib", 123L)
-                .setManifestPackageName("static.lib.pkg")
+        final ParsedPackage pkg = createBasicPackage("static.lib.pkg")
+                .setStaticSharedLibName("static.lib")
+                .setStaticSharedLibVersion(123L)
+                .hideAsParsed()
+                .setPackageName("static.lib.pkg.123")
                 .setVersionCodeMajor(1)
                 .setVersionCode(234)
                 .setBaseCodePath("/some/path.apk")
-                .addSplitCodePath("/some/other/path.apk")
-                .build();
+                .setSplitCodePaths(new String[] {"/some/other/path.apk"});
 
-        final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(
-                pkg).setUser(UserHandle.of(0)).build();
+        final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(pkg)
+                .setUser(UserHandle.of(0)).build();
 
 
         final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
@@ -240,15 +249,14 @@
 
     @Test
     public void installDynamicLibraries() throws Exception {
-        final PackageParser.Package pkg = createBasicPackage("dynamic.lib.pkg")
-                .setManifestPackageName("dynamic.lib.pkg")
+        final ParsedPackage pkg = createBasicPackage("dynamic.lib.pkg")
                 .addLibraryName("liba")
                 .addLibraryName("libb")
+                .hideAsParsed()
                 .setVersionCodeMajor(1)
                 .setVersionCode(234)
                 .setBaseCodePath("/some/path.apk")
-                .addSplitCodePath("/some/other/path.apk")
-                .build();
+                .setSplitCodePaths(new String[] {"/some/other/path.apk"});
 
         final PackageManagerService.ScanRequest scanRequest =
                 new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build();
@@ -290,15 +298,15 @@
                         .setVolumeUuid("someUuid")
                         .build();
 
-        final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
-                .setApplicationInfoVolumeUuid("someNewUuid")
-                .build();
+        final ParsedPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .hideAsParsed()
+                .setApplicationVolumeUuid(UUID_TWO.toString());
 
 
         final PackageManagerService.ScanResult scanResult = executeScan(
                 new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build());
 
-        assertThat(scanResult.pkgSetting.volumeUuid, is("someNewUuid"));
+        assertThat(scanResult.pkgSetting.volumeUuid, is(UUID_TWO.toString()));
     }
 
     @Test
@@ -306,10 +314,10 @@
         final PackageSetting pkgSetting =
                 createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build();
 
-        final PackageParser.Package basicPackage =
+        final ParsedPackage basicPackage =
                 createBasicPackage(DUMMY_PACKAGE_NAME)
-                        .setCpuAbiOVerride("testOverride")
-                        .build();
+                        .hideAsParsed()
+                        .setCpuAbiOverride("testOverride");
 
 
         final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder(
@@ -326,9 +334,9 @@
         final PackageSetting originalPkgSetting =
                 createBasicPackageSettingBuilder("original.package").build();
 
-        final PackageParser.Package basicPackage =
+        final ParsedPackage basicPackage =
                 createBasicPackage(DUMMY_PACKAGE_NAME)
-                        .build();
+                        .hideAsParsed();
 
 
         final PackageManagerService.ScanResult result =
@@ -336,7 +344,7 @@
                         .setOriginalPkgSetting(originalPkgSetting)
                         .build());
 
-        assertThat(result.request.pkg.packageName, is("original.package"));
+        assertThat(result.request.parsedPackage.getPackageName(), is("original.package"));
     }
 
     @Test
@@ -349,7 +357,7 @@
                         .build();
 
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setPkgSetting(existingPkgSetting)
                         .addScanFlag(SCAN_AS_FULL_APP)
                         .build();
@@ -370,7 +378,7 @@
                         .build();
 
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setPkgSetting(existingPkgSetting)
                         .addScanFlag(SCAN_AS_INSTANT_APP)
                         .build();
@@ -389,7 +397,7 @@
                         .build();
 
         final PackageManagerService.ScanRequest scanRequest =
-                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
                         .setPkgSetting(existingPkgSetting)
                         .setDisabledPkgSetting(existingPkgSetting)
                         .addScanFlag(SCAN_NEW_INSTALL)
@@ -397,15 +405,14 @@
 
         final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
 
-        assertThat(scanResult.request.pkg.applicationInfo.flags,
+        assertThat(scanResult.request.parsedPackage.getFlags(),
                 hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
     }
 
     @Test
     public void factoryTestFlagSet() throws Exception {
-        final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
-                .addPermissionRequest(Manifest.permission.FACTORY_TEST)
-                .build();
+        final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .addRequestedPermission(Manifest.permission.FACTORY_TEST);
 
         final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
                 createBasicScanRequestBuilder(basicPackage).build(),
@@ -413,15 +420,15 @@
                 true /*isUnderFactoryTest*/,
                 System.currentTimeMillis());
 
-        assertThat(scanResult.request.pkg.applicationInfo.flags,
+        assertThat(scanResult.request.parsedPackage.getFlags(),
                 hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
     }
 
     @Test
     public void scanSystemApp_isOrphanedTrue() throws Exception {
-        final PackageParser.Package pkg = createBasicPackage(DUMMY_PACKAGE_NAME)
-                .addApplicationInfoFlag(ApplicationInfo.FLAG_SYSTEM)
-                .build();
+        final ParsedPackage pkg = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .hideAsParsed()
+                .setSystem(true);
 
         final PackageManagerService.ScanRequest scanRequest =
                 createBasicScanRequestBuilder(pkg)
@@ -476,22 +483,29 @@
                 .setResourcePath(createResourcePath(packageName));
     }
 
-    private static ScanRequestBuilder createBasicScanRequestBuilder(PackageParser.Package pkg) {
+    private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) {
+        return new ScanRequestBuilder(pkg.hideAsParsed())
+                .setUser(UserHandle.of(0));
+    }
+
+    private static ScanRequestBuilder createBasicScanRequestBuilder(ParsedPackage pkg) {
         return new ScanRequestBuilder(pkg)
                 .setUser(UserHandle.of(0));
     }
 
-
-    private static PackageBuilder createBasicPackage(String packageName) {
-        return new PackageBuilder(packageName)
+    private static ParsingPackage createBasicPackage(String packageName) {
+        // TODO(b/135203078): Make this use PackageImpl.forParsing and separate the steps
+        return new PackageImpl(packageName, null, mock(TypedArray.class), false)
                 .setCodePath("/data/tmp/randompath")
                 .setApplicationInfoCodePath(createCodePath(packageName))
                 .setApplicationInfoResourcePath(createResourcePath(packageName))
-                .setApplicationInfoVolumeUuid("volumeUuid")
+                .setApplicationVolumeUuid(UUID_ONE.toString())
                 .setBaseCodePath("/data/tmp/randompath/base.apk")
-                .addUsesStaticLibrary("some.static.library", 234L)
-                .addUsesStaticLibrary("some.other.static.library", 456L)
-                .setApplicationInfoNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
+                .addUsesStaticLibrary("some.static.library")
+                .addUsesStaticLibraryVersion(234L)
+                .addUsesStaticLibrary("some.other.static.library")
+                .addUsesStaticLibraryVersion(456L)
+                .setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
                 .setVersionCodeMajor(1)
                 .setVersionCode(2345);
     }
@@ -503,20 +517,18 @@
         final PackageSetting pkgSetting = scanResult.pkgSetting;
         assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
 
-        final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo;
+        final ApplicationInfo applicationInfo = pkgSetting.pkg.toAppInfo();
         assertBasicApplicationInfo(scanResult, applicationInfo);
-
     }
 
     private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult,
             String packageName, boolean isInstant, PackageSetting pkgSetting) {
-        assertThat(pkgSetting.pkg.packageName, is(packageName));
+        assertThat(pkgSetting.pkg.getPackageName(), is(packageName));
         assertThat(pkgSetting.getInstantApp(0), is(isInstant));
         assertThat(pkgSetting.usesStaticLibraries,
                 arrayContaining("some.static.library", "some.other.static.library"));
         assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
-        assertThat(pkgSetting.pkg, is(scanResult.request.pkg));
-        assertThat(pkgSetting.pkg.mExtras, is(pkgSetting));
+        assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
         assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
         assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName))));
         assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
@@ -524,20 +536,21 @@
 
     private static void assertBasicApplicationInfo(PackageManagerService.ScanResult scanResult,
             ApplicationInfo applicationInfo) {
-        assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName));
+        assertThat(applicationInfo.processName,
+                is(scanResult.request.parsedPackage.getPackageName()));
 
         final int uid = applicationInfo.uid;
         assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM));
 
         final String calculatedCredentialId = Environment.getDataUserCePackageDirectory(
                 applicationInfo.volumeUuid, UserHandle.USER_SYSTEM,
-                scanResult.request.pkg.packageName).getAbsolutePath();
+                scanResult.request.parsedPackage.getPackageName()).getAbsolutePath();
         assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId));
         assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir));
     }
 
     private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) {
-        final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo;
+        final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.toAppInfo();
         assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
         assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary"));
 
@@ -549,7 +562,7 @@
     }
 
     private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) {
-        final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo;
+        final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.toAppInfo();
         assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
         assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("getRootDir"));
         assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index f0b0328..ddda10e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -37,8 +37,9 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.os.UserManager;
@@ -220,10 +221,10 @@
 
         final UserSystemPackageInstaller uspi = new UserSystemPackageInstaller(null, pkgFlgMap);
 
-        final PackageParser.Package pkg1 = new PackageParser.Package(packageName1);
-        final PackageParser.Package pkg2 = new PackageParser.Package(packageName2);
-        final PackageParser.Package pkg3 = new PackageParser.Package(packageName3);
-        final PackageParser.Package pkg4 = new PackageParser.Package(packageName4);
+        final AndroidPackage pkg1 = PackageImpl.forParsing(packageName1).hideAsParsed().hideAsFinal();
+        final AndroidPackage pkg2 = PackageImpl.forParsing(packageName2).hideAsParsed().hideAsFinal();
+        final AndroidPackage pkg3 = PackageImpl.forParsing(packageName3).hideAsParsed().hideAsFinal();
+        final AndroidPackage pkg4 = PackageImpl.forParsing(packageName4).hideAsParsed().hideAsFinal();
 
         // No implicit whitelist, so only install pkg1.
         boolean implicit = false;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 3a55c22..66a4946 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -22,8 +22,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.content.pm.ApplicationInfo;
 import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageImpl;
+import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackage;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -51,17 +54,18 @@
             DelegateLastClassLoader.class.getName();
 
     private static class TestData {
-        ApplicationInfo info;
+        AndroidPackage pkg;
         boolean[] pathsWithCode;
     }
 
     private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
-            boolean addSplitDependencies) {
-        ApplicationInfo ai = new ApplicationInfo();
+            boolean addSplitDependencies, boolean isolatedSplitLoading) {
         String codeDir = "/data/app/mock.android.com";
-        ai.setBaseCodePath(codeDir + "/base.dex");
-        ai.classLoaderName = baseClassLoader;
-        ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        ParsingPackage parsingPackage = PackageImpl.forParsing("mock.android.com")
+                .setClassLoaderName(baseClassLoader);
+
+        parsingPackage.setIsolatedSplitLoading(isolatedSplitLoading);
+
         boolean[] pathsWithCode;
         if (!addSplits) {
             pathsWithCode = new boolean[] {true};
@@ -70,7 +74,7 @@
             Arrays.fill(pathsWithCode, true);
             pathsWithCode[7] = false;  // config split
 
-            ai.setSplitCodePaths(new String[]{
+            String[] splitCodePaths = new String[]{
                     codeDir + "/base-1.dex",
                     codeDir + "/base-2.dex",
                     codeDir + "/base-3.dex",
@@ -78,32 +82,51 @@
                     codeDir + "/base-5.dex",
                     codeDir + "/base-6.dex",
                     codeDir + "/config-split-7.dex",
-                    codeDir + "/feature-no-deps.dex"});
+                    codeDir + "/feature-no-deps.dex"
+            };
 
-            ai.splitClassLoaderNames = new String[]{
-                    DELEGATE_LAST_CLASS_LOADER_NAME,
-                    DELEGATE_LAST_CLASS_LOADER_NAME,
-                    PATH_CLASS_LOADER_NAME,
-                    DEX_CLASS_LOADER_NAME,
-                    PATH_CLASS_LOADER_NAME,
-                    null,   // A null class loader name should default to PathClassLoader.
-                    null,   // The config split gets a null class loader.
-                    null};  // The feature split with no dependency and no specified class loader.
+            String[] splitNames = new String[splitCodePaths.length];
+            int[] splitRevisionCodes = new int[splitCodePaths.length];
+            SparseArray<int[]> splitDependencies = null;
+
             if (addSplitDependencies) {
-                ai.splitDependencies = new SparseArray<>(ai.splitClassLoaderNames.length + 1);
-                ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
-                ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2
-                ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4
-                ai.splitDependencies.put(3, new int[] {4}); // split 3 depends on 4
-                ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
-                ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
-                ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
+                splitDependencies = new SparseArray<>(splitCodePaths.length);
+                splitDependencies.put(0, new int[] {-1}); // base has no dependency
+                splitDependencies.put(1, new int[] {2}); // split 1 depends on 2
+                splitDependencies.put(2, new int[] {4}); // split 2 depends on 4
+                splitDependencies.put(3, new int[] {4}); // split 3 depends on 4
+                splitDependencies.put(4, new int[] {0}); // split 4 depends on base
+                splitDependencies.put(5, new int[] {0}); // split 5 depends on base
+                splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
                 // Do not add the config split to the dependency list.
                 // Do not add the feature split with no dependency to the dependency list.
             }
+
+            parsingPackage
+                    .asSplit(
+                            splitNames,
+                            splitCodePaths,
+                            splitRevisionCodes,
+                            splitDependencies
+                    )
+                    .setSplitClassLoaderName(0, DELEGATE_LAST_CLASS_LOADER_NAME)
+                    .setSplitClassLoaderName(1, DELEGATE_LAST_CLASS_LOADER_NAME)
+                    .setSplitClassLoaderName(2, PATH_CLASS_LOADER_NAME)
+                    .setSplitClassLoaderName(3, DEX_CLASS_LOADER_NAME)
+                    .setSplitClassLoaderName(4, PATH_CLASS_LOADER_NAME)
+                    // A null class loader name should default to PathClassLoader
+                    .setSplitClassLoaderName(5, null)
+                    // The config split gets a null class loader
+                    .setSplitClassLoaderName(6, null)
+                    // The feature split with no dependency and no specified class loader.
+                    .setSplitClassLoaderName(7, null);
         }
+
+        ParsedPackage parsedPackage = parsingPackage.hideAsParsed()
+                .setBaseCodePath(codeDir + "/base.dex");
+
         TestData data = new TestData();
-        data.info = ai;
+        data.pkg = parsedPackage.hideAsFinal();
         data.pathsWithCode = pathsWithCode;
         return data;
     }
@@ -118,11 +141,11 @@
 
     @Test
     public void testSplitChain() {
-        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -139,11 +162,11 @@
 
     @Test
     public void testSplitChainNoSplitDependencies() {
-        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -167,11 +190,9 @@
 
     @Test
     public void testSplitChainNoIsolationNoSharedLibrary() {
-        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
-        data.info.privateFlags = data.info.privateFlags
-                & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true, false);
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, null, data.pathsWithCode);
+                data.pkg, null, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals("PCL[]", contexts[0]);
@@ -192,9 +213,9 @@
     @Test
     public void testSplitChainNoSharedLibraries() {
         TestData data = createMockApplicationInfo(
-                DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
+                DELEGATE_LAST_CLASS_LOADER_NAME, true, true, true);
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, null, data.pathsWithCode);
+                data.pkg, null, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals("DLC[]", contexts[0]);
@@ -211,11 +232,11 @@
     @Test
     public void testSplitChainWithNullPrimaryClassLoader() {
         // A null classLoaderName should mean PathClassLoader.
-        TestData data = createMockApplicationInfo(null, true, true);
+        TestData data = createMockApplicationInfo(null, true, true, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -233,11 +254,11 @@
 
     @Test
     public void tesNoSplits() {
-        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -245,11 +266,11 @@
 
     @Test
     public void tesNoSplitsNullClassLoaderName() {
-        TestData data = createMockApplicationInfo(null, false, false);
+        TestData data = createMockApplicationInfo(null, false, false, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -258,11 +279,11 @@
     @Test
     public void tesNoSplitDelegateLast() {
         TestData data = createMockApplicationInfo(
-                DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
+                DELEGATE_LAST_CLASS_LOADER_NAME, false, false, true);
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("DLC[]{PCL[a.dex:b.dex]}", contexts[0]);
@@ -270,9 +291,9 @@
 
     @Test
     public void tesNoSplitsNoSharedLibraries() {
-        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+        TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false, true);
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, null, data.pathsWithCode);
+                data.pkg, null, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("PCL[]", contexts[0]);
@@ -281,9 +302,9 @@
     @Test
     public void tesNoSplitDelegateLastNoSharedLibraries() {
         TestData data = createMockApplicationInfo(
-                DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
+                DELEGATE_LAST_CLASS_LOADER_NAME, false, false, true);
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, null, data.pathsWithCode);
+                data.pkg, null, data.pathsWithCode);
 
         assertEquals(1, contexts.length);
         assertEquals("DLC[]", contexts[0]);
@@ -291,13 +312,13 @@
 
     @Test
     public void testContextWithNoCode() {
-        TestData data = createMockApplicationInfo(null, true, false);
+        TestData data = createMockApplicationInfo(null, true, false, true);
         Arrays.fill(data.pathsWithCode, false);
 
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals(null, contexts[0]);
@@ -312,12 +333,12 @@
 
     @Test
     public void testContextBaseNoCode() {
-        TestData data = createMockApplicationInfo(null, true, true);
+        TestData data = createMockApplicationInfo(null, true, true, true);
         data.pathsWithCode[0] = false;
         List<SharedLibraryInfo> sharedLibrary =
                 createMockSharedLibrary(new String[] {"a.dex", "b.dex"});
         String[] contexts = DexoptUtils.getClassLoaderContexts(
-                data.info, sharedLibrary, data.pathsWithCode);
+                data.pkg, sharedLibrary, data.pathsWithCode);
 
         assertEquals(9, contexts.length);
         assertEquals(null, contexts[0]);
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 0b8c2a55..0c5451f 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.rollback;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -271,15 +270,4 @@
 
         inOrder.verifyNoMoreInteractions();
     }
-
-    @Test
-    public void snapshotAddDataSavesSnapshottedUsersToInfo() {
-        Installer installer = mock(Installer.class);
-        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
-
-        PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar");
-        helper.snapshotAppData(5, info, new int[]{10, 11});
-
-        assertArrayEquals(info.getSnapshottedUsers().toArray(), new int[]{10, 11});
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index b5925a6..151b6e2 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -18,22 +18,48 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.util.IntArray;
 import android.util.SparseLongArray;
 
+import com.google.common.collect.Range;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.File;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 
 @RunWith(JUnit4.class)
 public class RollbackUnitTest {
 
+    private static final String PKG_1 = "test.testpackage.pkg1";
+    private static final String PKG_2 = "test.testpackage.pkg2";
+    private static final String PKG_3 = "com.blah.hello.three";
+    private static final String PKG_4 = "com.something.4pack";
+
+    @Mock private AppDataRollbackHelper mMockDataHelper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
     public void newEmptyStagedRollbackDefaults() {
         int rollbackId = 123;
@@ -61,82 +87,229 @@
     }
 
     @Test
-    public void rollbackStateChanges() {
+    public void rollbackMadeAvailable() {
         Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
 
         assertThat(rollback.isEnabling()).isTrue();
         assertThat(rollback.isAvailable()).isFalse();
         assertThat(rollback.isCommitted()).isFalse();
 
-        rollback.setAvailable();
+        Instant availableTime = Instant.now();
+        rollback.makeAvailable();
 
         assertThat(rollback.isEnabling()).isFalse();
         assertThat(rollback.isAvailable()).isTrue();
         assertThat(rollback.isCommitted()).isFalse();
 
-        rollback.setCommitted();
+        assertThat(rollback.getTimestamp()).isIn(Range.closed(availableTime, Instant.now()));
+    }
 
-        assertThat(rollback.isEnabling()).isFalse();
+    @Test
+    public void deletedRollbackCannotBeMadeAvailable() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+
+        rollback.delete(mMockDataHelper);
+
+        assertThat(rollback.isDeleted()).isTrue();
+
+        rollback.makeAvailable();
+
         assertThat(rollback.isAvailable()).isFalse();
-        assertThat(rollback.isCommitted()).isTrue();
+        assertThat(rollback.isDeleted()).isTrue();
     }
 
     @Test
     public void getPackageNamesAllAndJustApex() {
-        String pkg1 = "test.testpackage.pkg1";
-        String pkg2 = "test.testpackage.pkg2";
-        String pkg3 = "com.blah.hello.three";
-        String pkg4 = "com.something.4pack";
-
         Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
-        PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
-        PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 12, 10, true);
-        PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 12, 10, false);
-        PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 12, 10, true);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
+        PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
+        PackageRollbackInfo pkgInfo4 = newPkgInfoFor(PKG_4, 12, 1, true);
 
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
 
-        assertThat(rollback.getPackageNames()).containsExactly(pkg1, pkg2, pkg3, pkg4);
-        assertThat(rollback.getApexPackageNames()).containsExactly(pkg2, pkg4);
+        assertThat(rollback.getPackageNames()).containsExactly(PKG_1, PKG_2, PKG_3, PKG_4);
+        assertThat(rollback.getApexPackageNames()).containsExactly(PKG_2, PKG_4);
     }
 
     @Test
-    public void includesPackages() {
-        String pkg1 = "test.testpackage.pkg1";
-        String pkg2 = "test.testpackage.pkg2";
-        String pkg3 = "com.blah.hello.three";
-        String pkg4 = "com.something.4pack";
-
+    public void includesPackagesAfterEnable() {
         Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
-        PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
-        PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 18, 12, true);
-        PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 157, 156, false);
-        PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 99, 1, true);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
+        PackageRollbackInfo pkgInfo4 = newPkgInfoFor(PKG_4, 99, 1, true);
 
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
 
-        assertThat(rollback.includesPackage(pkg2)).isTrue();
-        assertThat(rollback.includesPackage(pkg3)).isTrue();
+        assertThat(rollback.includesPackage(PKG_2)).isTrue();
+        assertThat(rollback.includesPackage(PKG_3)).isTrue();
         assertThat(rollback.includesPackage("com.something.else")).isFalse();
 
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 12)).isFalse();
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 1)).isTrue();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_1, 12)).isFalse();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_1, 1)).isTrue();
 
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 18)).isFalse();
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 12)).isTrue();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_2, 18)).isFalse();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_2, 12)).isTrue();
 
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 157)).isFalse();
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 156)).isTrue();
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 15)).isTrue();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 157)).isFalse();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 156)).isTrue();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 15)).isTrue();
 
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 99)).isFalse();
-        assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 100)).isTrue();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_4, 99)).isFalse();
+        assertThat(rollback.includesPackageWithDifferentVersion(PKG_4, 100)).isTrue();
     }
 
-    private static PackageRollbackInfo pkgInfoFor(
+    @Test
+    public void snapshotWhenEnabling() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        assertThat(rollback.isEnabling()).isTrue();
+
+        int[] userIds = {4, 77};
+        rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+        // Data is snapshotted for the specified package.
+        verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
+        verify(mMockDataHelper, never())
+                .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+    }
+
+    @Test
+    public void snapshotWhenAvailable() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        rollback.makeAvailable();
+
+        assertThat(rollback.isAvailable()).isTrue();
+
+        int[] userIds = {4, 77};
+        rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+        // No data is snapshotted as rollback was not in the enabling state.
+        verify(mMockDataHelper, never())
+                .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+        verify(mMockDataHelper, never())
+                .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_2), any());
+    }
+
+    @Test
+    public void snapshotWhenDeleted() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        rollback.delete(mMockDataHelper);
+
+        assertThat(rollback.isDeleted()).isTrue();
+
+        int[] userIds = {4, 77};
+        rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+        // No data is snapshotted as rollback was not in the enabling state.
+        verify(mMockDataHelper, never())
+                .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+        verify(mMockDataHelper, never())
+                .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_2), any());
+    }
+
+    @Test
+    public void snapshotThenDelete() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        int[] userIds = {12, 18};
+        rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+        verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
+
+        rollback.delete(mMockDataHelper);
+
+        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(12));
+        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(18));
+
+        assertThat(rollback.isDeleted()).isTrue();
+    }
+
+    @Test
+    public void restoreUserDataDoesNothingIfNotInProgress() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        assertThat(rollback.isRestoreUserDataInProgress()).isFalse();
+
+        assertThat(rollback.restoreUserDataForPackageIfInProgress(
+                PKG_1, new int[] { 5 }, 333, "", mMockDataHelper)).isFalse();
+
+        verify(mMockDataHelper, never()).restoreAppData(anyInt(), any(), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void restoreUserDataDoesNothingIfPackageNotFound() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        rollback.setRestoreUserDataInProgress(true);
+        assertThat(rollback.isRestoreUserDataInProgress()).isTrue();
+
+        assertThat(rollback.restoreUserDataForPackageIfInProgress(
+                PKG_3, new int[] { 5 }, 333, "", mMockDataHelper)).isFalse();
+
+        verify(mMockDataHelper, never()).restoreAppData(anyInt(), any(), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void restoreUserDataRestoresIfInProgressAndPackageFound() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        rollback.setRestoreUserDataInProgress(true);
+        assertThat(rollback.isRestoreUserDataInProgress()).isTrue();
+
+        assertThat(rollback.restoreUserDataForPackageIfInProgress(
+                PKG_1, new int[] { 5, 7 }, 333, "blah", mMockDataHelper)).isTrue();
+
+        verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 5, 333, "blah");
+        verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 7, 333, "blah");
+    }
+
+    private static PackageRollbackInfo newPkgInfoFor(
             String packageName, long fromVersion, long toVersion, boolean isApex) {
         return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
                 new VersionedPackage(packageName, toVersion),
                 new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray());
     }
+
+    private static class PackageRollbackInfoForPackage implements
+            ArgumentMatcher<PackageRollbackInfo> {
+        private final String mPkg;
+
+        PackageRollbackInfoForPackage(String pkg) {
+            mPkg = pkg;
+        }
+
+        @Override
+        public boolean matches(PackageRollbackInfo pkgRollbackInfo) {
+            return pkgRollbackInfo.getPackageName().equals(mPkg);
+        }
+    }
+
+    private static PackageRollbackInfo pkgRollbackInfoFor(String pkg) {
+        return argThat(new PackageRollbackInfoForPackage(pkg));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
index dd2ee5c..d1ac19c 100644
--- a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.stats;
 
+import static com.android.server.stats.ProcfsMemoryUtil.parseCmdline;
 import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +26,8 @@
 
 import org.junit.Test;
 
+import java.io.ByteArrayOutputStream;
+
 /**
  * Build/Install/Run:
  *  atest FrameworksServicesTests:ProcfsMemoryUtilTest
@@ -100,4 +103,39 @@
         MemorySnapshot snapshot = parseMemorySnapshotFromStatus("");
         assertThat(snapshot).isNull();
     }
+
+    @Test
+    public void testParseCmdline_invalidValue() {
+        byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
+
+        assertThat(parseCmdline(bytesToString(nothing))).isEmpty();
+    }
+
+    @Test
+    public void testParseCmdline_correctValue_noNullBytes() {
+        assertThat(parseCmdline("com.google.app")).isEqualTo("com.google.app");
+    }
+
+    @Test
+    public void testParseCmdline_correctValue_withNullBytes() {
+        byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
+
+        assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test");
+
+        // test\0\0test
+        byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
+
+        assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test");
+    }
+
+    @Test
+    public void testParseCmdline_emptyContents() {
+        assertThat(parseCmdline("")).isEmpty();
+    }
+
+    private static String bytesToString(byte[] bytes) {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        output.write(bytes, 0, bytes.length);
+        return output.toString();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index dcab78e..3d87223 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -16,9 +16,10 @@
 
 package com.android.server.notification;
 
-import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
 
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
@@ -52,18 +53,18 @@
 
     @Test
     public void testPriorityOnlyMutingAllNotifications() {
-        ZenModeConfig config = getMutedNotificationsConfig();
-        assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+        ZenModeConfig config = getMutedRingerConfig();
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
 
         config.allowReminders = true;
-        assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         config.allowReminders = false;
 
         config.areChannelsBypassingDnd = true;
-        assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         config.areChannelsBypassingDnd = false;
 
-        assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
     }
 
     @Test
@@ -106,26 +107,26 @@
     @Test
     public void testPriorityOnlyMutingAll() {
         ZenModeConfig config = getMutedAllConfig();
-        assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
-        assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
 
         config.allowReminders = true;
-        assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
-        assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
         config.allowReminders = false;
 
         config.areChannelsBypassingDnd = true;
-        assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
-        assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
         config.areChannelsBypassingDnd = false;
 
         config.allowAlarms = true;
-        assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
-        assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
         config.allowAlarms = false;
 
-        assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
-        assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
     }
 
     @Test
@@ -200,14 +201,14 @@
         assertEquals(rule.zenMode, fromXml.zenMode);
     }
 
-    private ZenModeConfig getMutedNotificationsConfig() {
+    private ZenModeConfig getMutedRingerConfig() {
         ZenModeConfig config = new ZenModeConfig();
-        // Allow alarms, media, and system
+        // Allow alarms, media
         config.allowAlarms = true;
         config.allowMedia = true;
-        config.allowSystem = true;
 
-        // All notification sounds are not allowed
+        // All sounds that respect the ringer are not allowed
+        config.allowSystem = false;
         config.allowCalls = false;
         config.allowRepeatCallers = false;
         config.allowMessages = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 8936450..99771b9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -487,7 +487,6 @@
     public void testRingerAffectedStreamsPriorityOnly() {
         // in priority only mode:
         // ringtone, notification and system streams are affected by ringer mode
-        // UNLESS ringer is muted due to all the other priority only dnd sounds being muted
         mZenModeHelperSpy.mConfig.allowAlarms = true;
         mZenModeHelperSpy.mConfig.allowReminders = true;
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -503,8 +502,9 @@
         assertTrue((ringerModeAffectedStreams & (1 << AudioSystem.STREAM_ALARM)) == 0);
         assertTrue((ringerModeAffectedStreams & (1 << AudioSystem.STREAM_MUSIC)) == 0);
 
-        // special case: if ringer is muted (since all notification sounds cannot bypass)
-        // then system stream is not affected by ringer mode
+        // even when ringer is muted (since all ringer sounds cannot bypass DND),
+        // system stream is still affected by ringer mode
+        mZenModeHelperSpy.mConfig.allowSystem = false;
         mZenModeHelperSpy.mConfig.allowReminders = false;
         mZenModeHelperSpy.mConfig.allowCalls = false;
         mZenModeHelperSpy.mConfig.allowMessages = false;
@@ -519,7 +519,7 @@
         assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_NOTIFICATION))
                 != 0);
         assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_SYSTEM))
-                == 0);
+                != 0);
         assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_ALARM)) == 0);
         assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_MUSIC)) == 0);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 0110e94..bb80e5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,9 +20,11 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -124,6 +126,7 @@
         mDisplayContent = spy(mDisplayContent);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
+        when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
 
         synchronized (mWm.mGlobalLock) {
             mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
@@ -177,6 +180,7 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
+            assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
             mToken = mTarget.performDrag(
                     new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
                     data);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index 8d2a79b..2ad40f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -97,11 +97,6 @@
     }
 
     @Override
-    public SurfaceControl.Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
-        return this;
-    }
-
-    @Override
     public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop,
             Rect destFrame, @Surface.Rotation int orientation) {
         return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 714d2f2..eb351b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -57,6 +58,10 @@
         assertNotNull(mWm.mTaskPositioningController);
         mTarget = mWm.mTaskPositioningController;
 
+        when(mWm.mInputManager.transferTouchFocus(
+                any(InputChannel.class),
+                any(InputChannel.class))).thenReturn(true);
+
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.mInputChannel = new InputChannel();
         synchronized (mWm.mGlobalLock) {
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
index 3c916a6..608fc2c 100644
--- a/services/wifi/Android.bp
+++ b/services/wifi/Android.bp
@@ -5,6 +5,9 @@
         "java/**/*.java",
         "java/**/*.aidl",
     ],
+    aidl: {
+        local_include_dirs: ["java"]
+    },
     libs: [
         "services.net",
     ],
diff --git a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
index eadc726..3af4666 100644
--- a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
+++ b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
@@ -15,8 +15,9 @@
  */
 package android.net.wifi;
 
+import android.net.wifi.WifiApiServiceInfo;
+
 /** @hide */
 interface IWifiStackConnector {
-     IBinder retrieveApiServiceImpl(String serviceName);
-     boolean startApiService(String serviceName);
+     List<WifiApiServiceInfo> getWifiApiServiceInfos();
 }
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
similarity index 75%
rename from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
rename to services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
index 10d0551..45e4c69 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
+++ b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,13 +12,12 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package com.android.frameworks.coretests;
+package android.net.wifi;
 
-import android.app.Activity;
-
-public class TestActivity extends Activity {
-
+/** @hide */
+parcelable WifiApiServiceInfo {
+    String name;
+    IBinder binder;
 }
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
index 64af7a8..dcdfbc5 100644
--- a/services/wifi/java/android/net/wifi/WifiStackClient.java
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -21,12 +21,13 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityModuleConnector;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * Service used to communicate with the wifi stack, which could be running in a separate
  * module.
@@ -56,21 +57,23 @@
         @Override
         public void onModuleServiceConnected(IBinder service) {
             Log.i(TAG, "Wifi stack connected");
+            registerWifiStackService(service);
 
-            // spin up a new thread to not block system_server main thread
-            HandlerThread thread = new HandlerThread("InitWifiServicesThread");
-            thread.start();
-            thread.getThreadHandler().post(() -> {
-                registerWifiStackService(service);
-                IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
-                registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
-                registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
-                registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
-                registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
-                registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+            IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
 
-                thread.quitSafely();
-            });
+            List<WifiApiServiceInfo> wifiApiServiceInfos;
+            try {
+                wifiApiServiceInfos = connector.getWifiApiServiceInfos();
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed to getWifiApiServiceInfos()", e);
+            }
+
+            for (WifiApiServiceInfo wifiApiServiceInfo : wifiApiServiceInfos) {
+                String serviceName = wifiApiServiceInfo.name;
+                IBinder binder = wifiApiServiceInfo.binder;
+                Log.i(TAG, "Registering " + serviceName);
+                ServiceManager.addService(serviceName, binder);
+            }
         }
     }
 
@@ -81,32 +84,6 @@
         Log.i(TAG, "Wifi stack service registered");
     }
 
-    private void registerApiServiceAndStart(
-            IWifiStackConnector stackConnector, String serviceName) {
-        IBinder service = null;
-        try {
-            service = stackConnector.retrieveApiServiceImpl(serviceName);
-        } catch (RemoteException e) {
-            throw new RuntimeException("Failed to retrieve service impl " + serviceName, e);
-        }
-        if (service == null) {
-            Log.i(TAG, "Service " + serviceName + " not available");
-            return;
-        }
-        Log.i(TAG, "Registering " + serviceName);
-        ServiceManager.addService(serviceName, service);
-
-        boolean success = false;
-        try {
-            success = stackConnector.startApiService(serviceName);
-        } catch (RemoteException e) {
-            throw new RuntimeException("Failed to start service " + serviceName, e);
-        }
-        if (!success) {
-            throw new RuntimeException("Service " + serviceName + " start failed");
-        }
-    }
-
     /**
      * Start the wifi stack. Should be called only once on device startup.
      *
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 4fcda4d..7047498 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -302,6 +302,11 @@
     void setTestAutoModeApp(String packageName);
 
     /**
+     * @see TelecomServiceImpl#setSystemDialerPackage
+     */
+    void setSystemDialerPackage(in String packageName);
+
+    /**
      * @see TelecomServiceImpl#setTestDefaultDialer
      */
     void setTestDefaultDialer(in String packageName);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b449578..1d5c18d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1739,9 +1739,8 @@
             "allow_emergency_video_calls_bool";
 
     /**
-     * Flag indicating whether the carrier supports RCS presence indication for video calls.  When
-     * {@code true}, the carrier supports RCS presence indication for video calls.  When presence
-     * is supported, the device should use the
+     * Flag indicating whether the carrier supports RCS presence indication for
+     * User Capability Exchange (UCE).  When presence is supported, the device should use the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
      * whether each contact supports video calling.  The UI is made aware that presence is enabled
@@ -1752,6 +1751,12 @@
     public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
 
     /**
+     * Flag indicating whether the carrier supports RCS SIP OPTIONS indication for
+     * User Capability Exchange (UCE).
+     */
+    public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
+
+    /**
      * The duration in seconds that platform call and message blocking is disabled after the user
      * contacts emergency services. Platform considers values for below cases:
      *  1) 0 <= VALUE <= 604800(one week): the value will be used as the duration directly.
@@ -3435,6 +3440,7 @@
         sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
         sDefaults.putInt(KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 0);
         sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_RCS_SIP_OPTIONS_BOOL, false);
         sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
         sDefaults.putInt(
                 KEY_CDMA_ROAMING_MODE_INT, TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 1ffed25..1e1e3da 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
@@ -366,6 +367,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER           = 0x10000000;
 
@@ -378,6 +380,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER            = 0x20000000;
 
@@ -869,6 +872,7 @@
      *                              to.
      * @hide
      */
+    @SystemApi
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
         // default implementation empty
     }
@@ -879,6 +883,7 @@
      * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
      * @hide
      */
+    @SystemApi
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
         // default implementation empty
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index fe76b7c..d7a7af1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -93,6 +93,10 @@
     void notifyActiveDataSubIdChanged(int activeDataSubId);
     void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
     void notifyEmergencyNumberList(in int phoneId, in int subId);
+    void notifyOutgoingEmergencyCall(in int phoneId, in int subId,
+            in EmergencyNumber emergencyNumber);
+    void notifyOutgoingEmergencySms(in int phoneId, in int subId,
+            in EmergencyNumber emergencyNumber);
     void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
             int callNetworkType);
     void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
new file mode 100644
index 0000000..022f798
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class CloseImeAutoOpenWindowToAppTest extends CloseImeWindowToAppTest {
+
+    public CloseImeAutoOpenWindowToAppTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @Before
+    public void runTransition() {
+        run(editTextLoseFocusToApp((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns().build());
+    }
+
+    @FlakyTest(bugId = 141458352)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        super.checkVisibility_imeLayerBecomesInvisible();
+    }
+
+    @FlakyTest(bugId = 141458352)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_imeAppLayerIsAlwaysVisible() {
+        super.checkVisibility_imeAppLayerIsAlwaysVisible();
+    }
+
+    @FlakyTest(bugId = 141458352)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_imeAppWindowIsAlwaysVisible() {
+        super.checkVisibility_imeAppWindowIsAlwaysVisible();
+    }
+
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java
new file mode 100644
index 0000000..d6f994b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.LargeTest;
+
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class CloseImeAutoOpenWindowToHomeTest extends CloseImeWindowToHomeTest {
+
+    public CloseImeAutoOpenWindowToHomeTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        mTestApp = new ImeAppAutoFocusHelper(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @Before
+    public void runTransition() {
+        run(editTextLoseFocusToHome((ImeAppAutoFocusHelper) mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns().build());
+    }
+
+    @FlakyTest(bugId = 141458352)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_imeWindowBecomesInvisible() {
+        super.checkVisibility_imeWindowBecomesInvisible();
+    }
+
+    @FlakyTest(bugId = 141458352)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        super.checkVisibility_imeLayerBecomesInvisible();
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
index 9deb977..28da3af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
@@ -17,37 +17,39 @@
 package com.android.server.wm.flicker;
 
 import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-
-import android.platform.helpers.IAppHelper;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test IME window closing back to app window transitions.
  * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeWindowToAppTest extends FlickerTestBase {
+public class CloseImeWindowToAppTest extends NonRotationTestBase {
 
-    private static final String IME_WINDOW_TITLE = "InputMethod";
-    private IAppHelper mImeTestApp = new StandardAppHelper(
-            InstrumentationRegistry.getInstrumentation(),
-            "com.android.server.wm.flicker.testapp", "ImeApp");
+    static final String IME_WINDOW_TITLE = "InputMethod";
+
+    public CloseImeWindowToAppTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+    }
 
     @Before
     public void runTransition() {
-        super.runTransition(editTextLoseFocusToApp(mUiDevice)
+        run(editTextLoseFocusToApp((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
                 .includeJankyRuns().build());
     }
 
@@ -63,20 +65,14 @@
     @Test
     public void checkVisibility_imeAppLayerIsAlwaysVisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(mImeTestApp.getPackage())
+                .showsLayer(mTestApp.getPackage())
                 .forAllEntries());
     }
 
     @Test
     public void checkVisibility_imeAppWindowIsAlwaysVisible() {
         checkResults(result -> WmTraceSubject.assertThat(result)
-                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .showsAppWindowOnTop(mTestApp.getPackage())
                 .forAllEntries());
     }
-
-    @Test
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
-                getDisplayBounds()).forAllEntries());
-    }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
index cce5a2a..fc6719e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -17,37 +17,39 @@
 package com.android.server.wm.flicker;
 
 import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-
-import android.platform.helpers.IAppHelper;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test IME window closing to home transitions.
  * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class CloseImeWindowToHomeTest extends FlickerTestBase {
+public class CloseImeWindowToHomeTest extends NonRotationTestBase {
 
-    private static final String IME_WINDOW_TITLE = "InputMethod";
-    private IAppHelper mImeTestApp = new StandardAppHelper(
-            InstrumentationRegistry.getInstrumentation(),
-            "com.android.server.wm.flicker.testapp", "ImeApp");
+    static final String IME_WINDOW_TITLE = "InputMethod";
+
+    public CloseImeWindowToHomeTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+    }
 
     @Before
     public void runTransition() {
-        super.runTransition(editTextLoseFocusToHome(mUiDevice)
+        run(editTextLoseFocusToHome((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
                 .includeJankyRuns().build());
     }
 
@@ -72,24 +74,18 @@
     @Test
     public void checkVisibility_imeAppLayerBecomesInvisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(mImeTestApp.getPackage())
+                .showsLayer(mTestApp.getPackage())
                 .then()
-                .hidesLayer(mImeTestApp.getPackage())
+                .hidesLayer(mTestApp.getPackage())
                 .forAllEntries());
     }
 
     @Test
     public void checkVisibility_imeAppWindowBecomesInvisible() {
         checkResults(result -> WmTraceSubject.assertThat(result)
-                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .showsAppWindowOnTop(mTestApp.getPackage())
                 .then()
-                .hidesAppWindowOnTop(mImeTestApp.getPackage())
+                .hidesAppWindowOnTop(mTestApp.getPackage())
                 .forAllEntries());
     }
-
-    @Test
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
-                getDisplayBounds()).forAllEntries());
-    }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
index 1d44ea4..fd31aa5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -37,10 +37,10 @@
 import android.util.Rational;
 import android.view.Surface;
 
-import androidx.test.InstrumentationRegistry;
-
 import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
 import com.android.server.wm.flicker.helpers.AutomationUtils;
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
+import com.android.server.wm.flicker.helpers.PipAppHelper;
 
 /**
  * Collection of common transitions which can be used to test different apps or scenarios.
@@ -73,26 +73,17 @@
         }
     }
 
-    private static void clickEditTextWidget(UiDevice device, IAppHelper testApp) {
-        UiObject2 editText = device.findObject(By.res(testApp.getPackage(), "plain_text_input"));
-        editText.click();
-        sleep(500);
-    }
-
-    private static void clickEnterPipButton(UiDevice device, IAppHelper testApp) {
-        UiObject2 enterPipButton = device.findObject(By.res(testApp.getPackage(), "enter_pip"));
-        enterPipButton.click();
-        sleep(500);
-    }
-
     static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
-            device) {
+            device, int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("OpenAppWarm_" + testApp.getLauncherName())
+                .withTag("OpenAppWarm_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> setRotation(device, beginRotation))
                 .runBeforeAll(testApp::open)
                 .runBefore(device::pressHome)
                 .runBefore(device::waitForIdle)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .run(testApp::open)
                 .runAfterAll(testApp::exit)
                 .runAfterAll(AutomationUtils::setDefaultWait)
@@ -127,16 +118,19 @@
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder getOpenAppCold(IAppHelper testApp,
-            UiDevice device) {
+    static TransitionBuilder openAppCold(IAppHelper testApp,
+            UiDevice device, int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("OpenAppCold_" + testApp.getLauncherName())
+                .withTag("OpenAppCold_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBeforeAll(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::exit)
                 .runBefore(device::waitForIdle)
                 .run(testApp::open)
                 .runAfterAll(testApp::exit)
+                .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
                 .repeat(ITERATIONS);
     }
 
@@ -201,28 +195,31 @@
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder editTextSetFocus(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "ImeApp");
+    static TransitionBuilder editTextSetFocus(ImeAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("editTextSetFocus_" + testApp.getLauncherName())
+                .withTag("editTextSetFocus_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
-                .run(() -> clickEditTextWidget(device, testApp))
+                .run(() -> testApp.clickEditTextWidget(device))
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, IAppHelper testAppBottom,
-            UiDevice device, Rational startRatio, Rational stopRatio) {
+    static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, ImeAppHelper testAppBottom,
+            UiDevice device, int beginRotation, Rational startRatio, Rational stopRatio) {
         String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_"
                 + testAppBottom.getLauncherName() + "_"
                 + startRatio.toString().replace("/", ":") + "_to_"
-                + stopRatio.toString().replace("/", ":");
+                + stopRatio.toString().replace("/", ":") + "_"
+                + rotationToString(beginRotation);
         return TransitionRunner.newBuilder()
                 .withTag(testTag)
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> setRotation(device, beginRotation))
                 .runBeforeAll(() -> clearRecents(device))
                 .runBefore(testAppBottom::open)
                 .runBefore(device::pressHome)
@@ -234,6 +231,7 @@
                             By.res(device.getLauncherPackageName(), "snapshot"));
                     snapshot.click();
                 })
+                .runBefore(() -> testAppBottom.clickEditTextWidget(device))
                 .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
                 .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
                 .runAfter(() -> exitSplitScreen(device))
@@ -243,74 +241,70 @@
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder editTextLoseFocusToHome(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "ImeApp");
+    static TransitionBuilder editTextLoseFocusToHome(ImeAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName())
+                .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
-                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .runBefore(() -> testApp.clickEditTextWidget(device))
                 .run(device::pressHome)
                 .run(device::waitForIdle)
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder editTextLoseFocusToApp(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "ImeApp");
+    static TransitionBuilder editTextLoseFocusToApp(ImeAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName())
+                .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
-                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .runBefore(() -> testApp.clickEditTextWidget(device))
                 .run(device::pressBack)
                 .run(device::waitForIdle)
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder enterPipMode(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "PipApp");
+    static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device) {
         return TransitionRunner.newBuilder()
                 .withTag("enterPipMode_" + testApp.getLauncherName())
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
                 .runBefore(testApp::open)
-                .run(() -> clickEnterPipButton(device, testApp))
+                .run(() -> testApp.clickEnterPipButton(device))
                 .runAfter(() -> closePipWindow(device))
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder exitPipModeToHome(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "PipApp");
+    static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device) {
         return TransitionRunner.newBuilder()
                 .withTag("exitPipModeToHome_" + testApp.getLauncherName())
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
                 .runBefore(testApp::open)
-                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .runBefore(() -> testApp.clickEnterPipButton(device))
                 .run(() -> closePipWindow(device))
                 .run(device::waitForIdle)
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder exitPipModeToApp(UiDevice device) {
-        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "PipApp");
+    static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device) {
         return TransitionRunner.newBuilder()
                 .withTag("exitPipModeToApp_" + testApp.getLauncherName())
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
                 .runBefore(testApp::open)
-                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .runBefore(() -> testApp.clickEnterPipButton(device))
                 .run(() -> expandPipWindow(device))
                 .run(device::waitForIdle)
                 .runAfterAll(testApp::exit)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
index 9836655..8f0177c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -25,6 +25,9 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
+import com.android.server.wm.flicker.helpers.PipAppHelper;
+
 import org.junit.FixMethodOrder;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -44,23 +47,25 @@
     private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
 
     /**
-     * atest FlickerTest:DebugTests#openAppCold
+     * atest FlickerTests:DebugTest#openAppCold
      */
     @Test
     public void openAppCold() {
-        CommonTransitions.getOpenAppCold(testApp, uiDevice).recordAllRuns().build().run();
+        CommonTransitions.openAppCold(testApp, uiDevice, Surface.ROTATION_0)
+                .recordAllRuns().build().run();
     }
 
     /**
-     * atest FlickerTest:DebugTests#openAppWarm
+     * atest FlickerTests:DebugTest#openAppWarm
      */
     @Test
     public void openAppWarm() {
-        CommonTransitions.openAppWarm(testApp, uiDevice).recordAllRuns().build().run();
+        CommonTransitions.openAppWarm(testApp, uiDevice, Surface.ROTATION_0)
+                .recordAllRuns().build().run();
     }
 
     /**
-     * atest FlickerTest:DebugTests#changeOrientationFromNaturalToLeft
+     * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft
      */
     @Test
     public void changeOrientationFromNaturalToLeft() {
@@ -69,7 +74,7 @@
     }
 
     /**
-     * atest FlickerTest:DebugTests#closeAppWithBackKey
+     * atest FlickerTests:DebugTest#closeAppWithBackKey
      */
     @Test
     public void closeAppWithBackKey() {
@@ -77,7 +82,7 @@
     }
 
     /**
-     * atest FlickerTest:DebugTests#closeAppWithHomeKey
+     * atest FlickerTests:DebugTest#closeAppWithHomeKey
      */
     @Test
     public void closeAppWithHomeKey() {
@@ -85,7 +90,7 @@
     }
 
     /**
-     * atest FlickerTest:DebugTests#openAppToSplitScreen
+     * atest FlickerTests:DebugTest#openAppToSplitScreen
      */
     @Test
     public void openAppToSplitScreen() {
@@ -94,7 +99,7 @@
     }
 
     /**
-     * atest FlickerTest:DebugTests#splitScreenToLauncher
+     * atest FlickerTests:DebugTest#splitScreenToLauncher
      */
     @Test
     public void splitScreenToLauncher() {
@@ -104,70 +109,80 @@
     }
 
     /**
-     * atest FlickerTest:DebugTests#resizeSplitScreen
+     * atest FlickerTests:DebugTest#resizeSplitScreen
      */
     @Test
     public void resizeSplitScreen() {
-        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "ImeApp");
-        CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
-                new Rational(2, 3)).includeJankyRuns().recordEachRun().build().run();
+        ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, Surface.ROTATION_0,
+                new Rational(1, 3), new Rational(2, 3))
+                .includeJankyRuns().recordEachRun().build().run();
     }
 
     // IME tests
 
     /**
-     * atest FlickerTest:DebugTests#editTextSetFocus
+     * atest FlickerTests:DebugTest#editTextSetFocus
      */
     @Test
     public void editTextSetFocus() {
-        CommonTransitions.editTextSetFocus(uiDevice).includeJankyRuns().recordEachRun()
+        ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0)
+                .includeJankyRuns().recordEachRun()
                 .build().run();
     }
 
     /**
-     * atest FlickerTest:DebugTests#editTextLoseFocusToHome
+     * atest FlickerTests:DebugTest#editTextLoseFocusToHome
      */
     @Test
     public void editTextLoseFocusToHome() {
-        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+        ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
+                .includeJankyRuns().recordEachRun()
                 .build().run();
     }
 
     /**
-     * atest FlickerTest:DebugTests#editTextLoseFocusToApp
+     * atest FlickerTests:DebugTest#editTextLoseFocusToApp
      */
     @Test
     public void editTextLoseFocusToApp() {
-        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+        ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
+                .includeJankyRuns().recordEachRun()
                 .build().run();
     }
 
     // PIP tests
 
     /**
-     * atest FlickerTest:DebugTests#enterPipMode
+     * atest FlickerTests:DebugTest#enterPipMode
      */
     @Test
     public void enterPipMode() {
-        CommonTransitions.enterPipMode(uiDevice).includeJankyRuns().recordEachRun().build().run();
-    }
-
-    /**
-     * atest FlickerTest:DebugTests#exitPipModeToHome
-     */
-    @Test
-    public void exitPipModeToHome() {
-        CommonTransitions.exitPipModeToHome(uiDevice).includeJankyRuns().recordEachRun()
+        PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.enterPipMode(testApp, uiDevice).includeJankyRuns().recordEachRun()
                 .build().run();
     }
 
     /**
-     * atest FlickerTest:DebugTests#exitPipModeToApp
+     * atest FlickerTests:DebugTest#exitPipModeToHome
+     */
+    @Test
+    public void exitPipModeToHome() {
+        PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.exitPipModeToHome(testApp, uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTests:DebugTest#exitPipModeToApp
      */
     @Test
     public void exitPipModeToApp() {
-        CommonTransitions.exitPipModeToApp(uiDevice).includeJankyRuns().recordEachRun()
+        PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+        CommonTransitions.exitPipModeToApp(testApp, uiDevice).includeJankyRuns().recordEachRun()
                 .build().run();
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
index 6e8e0c3..883d59e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -98,7 +98,7 @@
     /**
      * Runs a transition, returns a cached result if the transition has run before.
      */
-    void runTransition(TransitionRunner transition) {
+    void run(TransitionRunner transition) {
         if (transitionResults.containsKey(transition.getTestTag())) {
             mResults = transitionResults.get(transition.getTestTag());
             return;
@@ -111,6 +111,13 @@
     }
 
     /**
+     * Runs a transition, returns a cached result if the transition has run before.
+     */
+    void runTransition(TransitionRunner transition) {
+        run(transition);
+    }
+
+    /**
      * Goes through a list of transition results and checks assertions on each result.
      */
     void checkResults(Consumer<TransitionResult> assertion) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
new file mode 100644
index 0000000..54941dc
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.graphics.Rect;
+import android.view.Surface;
+
+import androidx.test.filters.FlakyTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public abstract class NonRotationTestBase extends FlickerTestBase {
+
+    int mBeginRotation;
+
+    public NonRotationTestBase(String beginRotationName, int beginRotation) {
+        this.mBeginRotation = beginRotation;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> getParams() {
+        int[] supportedRotations =
+                {Surface.ROTATION_0, Surface.ROTATION_90};
+        Collection<Object[]> params = new ArrayList<>();
+
+        for (int begin : supportedRotations) {
+            params.add(new Object[]{rotationToString(begin), begin});
+        }
+
+        return params;
+    }
+
+    @FlakyTest(bugId = 141361128)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        Rect displayBounds = getDisplayBounds(mBeginRotation);
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                displayBounds).forAllEntries());
+    }
+
+    @FlakyTest(bugId = 141361128)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @FlakyTest(bugId = 141361128)
+    @Ignore("Waiting bug feedback")
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
index 8d99054..efdfaee 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -16,14 +16,12 @@
 
 package com.android.server.wm.flicker;
 
-import static com.android.server.wm.flicker.CommonTransitions.getOpenAppCold;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.CommonTransitions.openAppCold;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -31,36 +29,28 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test cold launch app from launcher.
  * To run this test: {@code atest FlickerTests:OpenAppColdTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppColdTest extends FlickerTestBase {
+public class OpenAppColdTest extends NonRotationTestBase {
 
-    public OpenAppColdTest() {
+    public OpenAppColdTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
         this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
                 "com.android.server.wm.flicker.testapp", "SimpleApp");
     }
 
     @Before
     public void runTransition() {
-        super.runTransition(getOpenAppCold(mTestApp, mUiDevice).build());
-    }
-
-    @Test
-    public void checkVisibility_navBarWindowIsAlwaysVisible() {
-        checkResults(result -> assertThat(result)
-                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
-        checkResults(result -> assertThat(result)
-                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+        run(openAppCold(mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns().build());
     }
 
     @Test
@@ -72,6 +62,8 @@
                 .forAllEntries());
     }
 
+    @FlakyTest(bugId = 140855415)
+    @Ignore("Waiting bug feedback")
     @Test
     public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
         checkResults(result -> assertThat(result)
@@ -83,26 +75,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 141235985)
-    @Ignore("Waiting bug feedback")
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
-                getDisplayBounds()).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
     public void checkVisibility_wallpaperLayerBecomesInvisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
                 .showsLayer("Wallpaper")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
index e8702c2..7ce6315 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -17,13 +17,11 @@
 package com.android.server.wm.flicker;
 
 import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -31,36 +29,28 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test warm launch app.
  * To run this test: {@code atest FlickerTests:OpenAppWarmTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppWarmTest extends FlickerTestBase {
+public class OpenAppWarmTest extends NonRotationTestBase {
 
-    public OpenAppWarmTest() {
+    public OpenAppWarmTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
         this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
                 "com.android.server.wm.flicker.testapp", "SimpleApp");
     }
 
     @Before
     public void runTransition() {
-        super.runTransition(openAppWarm(mTestApp, mUiDevice).includeJankyRuns().build());
-    }
-
-    @Test
-    public void checkVisibility_navBarIsAlwaysVisible() {
-        checkResults(result -> assertThat(result)
-                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarIsAlwaysVisible() {
-        checkResults(result -> assertThat(result)
-                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+        super.runTransition(openAppWarm(mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns().build());
     }
 
     @Test
@@ -72,6 +62,8 @@
                 .forAllEntries());
     }
 
+    @FlakyTest(bugId = 140855415)
+    @Ignore("Waiting bug feedback")
     @Test
     public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
         checkResults(result -> assertThat(result)
@@ -82,26 +74,6 @@
                 .forAllEntries());
     }
 
-    @FlakyTest(bugId = 141235985)
-    @Ignore("Waiting bug feedback")
-    @Test
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
-                getDisplayBounds()).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
     @Test
     public void checkVisibility_wallpaperLayerBecomesInvisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
index 9f5cfce..91d4a05 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -17,31 +17,39 @@
 package com.android.server.wm.flicker;
 
 import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test IME window opening transitions.
  * To run this test: {@code atest FlickerTests:OpenImeWindowTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenImeWindowTest extends FlickerTestBase {
+public class OpenImeWindowTest extends NonRotationTestBase {
 
     private static final String IME_WINDOW_TITLE = "InputMethod";
 
+    public OpenImeWindowTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+    }
+
     @Before
     public void runTransition() {
-        super.runTransition(editTextSetFocus(mUiDevice)
+        run(editTextSetFocus((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
                 .includeJankyRuns().build());
     }
 
@@ -62,10 +70,4 @@
                 .showsLayer(IME_WINDOW_TITLE)
                 .forAllEntries());
     }
-
-    @Test
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
-                getDisplayBounds()).forAllEntries());
-    }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
index 1031baf..29b6240 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -24,13 +24,13 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.graphics.Rect;
-import android.platform.helpers.IAppHelper;
 import android.util.Rational;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
@@ -38,57 +38,48 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test split screen resizing window transitions.
  * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 140856143)
+@FlakyTest(bugId = 140854698)
 @Ignore("Waiting bug feedback")
-public class ResizeSplitScreenTest extends FlickerTestBase {
+public class ResizeSplitScreenTest extends NonRotationTestBase {
 
-    public ResizeSplitScreenTest() {
+    private static String sSimpleActivity = "SimpleActivity";
+    private static String sImeActivity = "ImeActivity";
+
+    public ResizeSplitScreenTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
         this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
                 "com.android.server.wm.flicker.testapp", "SimpleApp");
     }
 
     @Before
     public void runTransition() {
-        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry
-                .getInstrumentation(),
-                "com.android.server.wm.flicker.testapp", "ImeApp");
-        super.runTransition(resizeSplitScreen(mTestApp, bottomApp, mUiDevice, new Rational(1, 3),
-                new Rational(2, 3)).includeJankyRuns().build());
-    }
-
-    @Test
-    public void checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
-                .forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(STATUS_BAR_WINDOW_TITLE)
-                .forAllEntries());
+        ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+        run(resizeSplitScreen(mTestApp, bottomApp, mUiDevice, mBeginRotation,
+                new Rational(1, 3), new Rational(2, 3))
+                .includeJankyRuns().build());
     }
 
     @Test
     public void checkVisibility_topAppLayerIsAlwaysVisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer("SimpleActivity")
+                .showsLayer(sSimpleActivity)
                 .forAllEntries());
     }
 
     @Test
     public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer("ImeActivity")
+                .showsLayer(sImeActivity)
                 .forAllEntries());
     }
 
@@ -149,11 +140,11 @@
                     displayBounds.bottom - getNavigationBarHeight());
 
             LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+                    .hasVisibleRegion(sSimpleActivity, startingTopAppBounds)
                     .atTheEnd();
 
             LayersTraceSubject.assertThat(result)
-                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+                    .hasVisibleRegion(sImeActivity, startingBottomAppBounds)
                     .atTheEnd();
         });
     }
@@ -175,14 +166,14 @@
     @Test
     public void checkVisibility_topAppWindowIsAlwaysVisible() {
         checkResults(result -> WmTraceSubject.assertThat(result)
-                .showsAppWindow("SimpleActivity")
+                .showsAppWindow(sSimpleActivity)
                 .forAllEntries());
     }
 
     @Test
     public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
         checkResults(result -> WmTraceSubject.assertThat(result)
-                .showsAppWindow("ImeActivity")
+                .showsAppWindow(sImeActivity)
                 .forAllEntries());
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java
new file mode 100644
index 0000000..42977f5
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import android.app.Instrumentation;
+
+import com.android.server.wm.flicker.StandardAppHelper;
+
+public abstract class FlickerAppHelper extends StandardAppHelper {
+
+    static int sFindTimeout = 10000;
+    static String sFlickerPackage = "com.android.server.wm.flicker.testapp";
+
+    public FlickerAppHelper(Instrumentation instr, String launcherName) {
+        super(instr, sFlickerPackage, launcherName);
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java
new file mode 100644
index 0000000..56e1118
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.UiDevice;
+
+public class ImeAppAutoFocusHelper extends ImeAppHelper {
+
+    public ImeAppAutoFocusHelper(Instrumentation instr) {
+        super(instr, "ImeAppAutoFocus");
+    }
+
+    public void clickEditTextWidget(UiDevice device) {
+        // do nothing (the app is focused automatically)
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java
new file mode 100644
index 0000000..098fd6d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import static android.os.SystemClock.sleep;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+
+public class ImeAppHelper extends FlickerAppHelper {
+
+    ImeAppHelper(Instrumentation instr, String launcherName) {
+        super(instr, launcherName);
+    }
+
+    public ImeAppHelper(Instrumentation instr) {
+        this(instr, "ImeApp");
+    }
+
+    public void clickEditTextWidget(UiDevice device) {
+        UiObject2 editText = device.findObject(By.res(getPackage(), "plain_text_input"));
+        editText.click();
+        sleep(500);
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
new file mode 100644
index 0000000..d00e11b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import static com.android.server.wm.flicker.helpers.AutomationUtils.getPipWindowSelector;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+public class PipAppHelper extends FlickerAppHelper {
+
+    public PipAppHelper(Instrumentation instr) {
+        super(instr, "PipApp");
+    }
+
+    public void clickEnterPipButton(UiDevice device) {
+        UiObject2 enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"));
+        enterPipButton.click();
+        UiObject2 pipWindow = device.wait(Until.findObject(getPipWindowSelector()), sFindTimeout);
+
+        if (pipWindow == null) {
+            throw new RuntimeException("Unable to find PIP window");
+        }
+    }
+
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index b694172..0fe9682 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -38,6 +38,15 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".ImeActivityAutoFocus"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
+                  android:windowSoftInputMode="stateVisible"
+                  android:label="ImeAppAutoFocus">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
         <activity android:name=".PipActivity"
                   android:resizeableActivity="true"
                   android:supportsPictureInPicture="true"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index d5eb023..4708cfd 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:focusableInTouchMode="true"
     android:background="@android:color/holo_green_light">
     <EditText android:id="@+id/plain_text_input"
               android:layout_height="wrap_content"
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
similarity index 62%
rename from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
rename to tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index 1e721aa..05da717 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,19 +12,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package com.android.frameworks.coretests;
+package com.android.server.wm.flicker.testapp;
 
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+import android.widget.EditText;
 
-public class SecondChildTestService extends Service {
+public class ImeActivityAutoFocus extends ImeActivity {
 
     @Override
-    public IBinder onBind(Intent intent) {
-        return null;
+    protected void onStart() {
+        super.onStart();
+
+        EditText editTextField = findViewById(R.id.plain_text_input);
+        editTextField.requestFocus();
     }
 }
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 2adbb06..b4f6e99 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -18,7 +18,6 @@
 
 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 import static android.system.OsConstants.IPPROTO_TCP;
@@ -28,6 +27,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -45,7 +45,6 @@
 import libcore.util.HexEncoding;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -152,9 +151,13 @@
 
     private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
                                          InetSocketAddress remote, boolean expectSuccess) {
-        final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
         final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
-        assertEquals(expectedUid, uid);
+
+        if (expectSuccess) {
+            assertEquals(Process.myUid(), uid);
+        } else {
+            assertNotEquals(Process.myUid(), uid);
+        }
     }
 
     private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
@@ -165,11 +168,11 @@
         return localPort;
     }
 
+    /**
+     * Create a test connection for UDP and TCP sockets and verify that this
+     * {protocol, local, remote} socket result in receiving a valid UID.
+     */
     public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
-        /**
-         * For TCP connections, create a test connection and verify that this
-         * {protocol, local, remote} socket result in receiving a valid UID.
-         */
         TcpConnection tcp = new TcpConnection(to, from);
         checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
         checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
@@ -177,20 +180,14 @@
         checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
         tcp.close();
 
-        /**
-         * For UDP connections, either a complete match {protocol, local, remote} or a
-         * partial match {protocol, local} should return a valid UID.
-         */
         UdpConnection udp = new UdpConnection(to,from);
         checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
-        checkConnectionOwnerUid(udp.protocol, udp.local, new InetSocketAddress(0), true);
         checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
         checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
                 udp.remote, false);
         udp.close();
     }
 
-    @Ignore
     @Test
     public void testGetConnectionOwnerUid() throws Exception {
         checkGetConnectionOwnerUid("::", null);
@@ -203,6 +200,16 @@
         checkGetConnectionOwnerUid("::1", "::1");
     }
 
+    /* Verify fix for b/141603906 */
+    @Test
+    public void testB141603906() throws Exception {
+        final InetSocketAddress src = new InetSocketAddress(0);
+        final InetSocketAddress dst = new InetSocketAddress(0);
+        for (int i = 1; i <= 100000; i++) {
+            mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
+        }
+    }
+
     // Hexadecimal representation of InetDiagReqV2 request.
     private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
             // struct nlmsghdr
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index cd2bd26..7029218 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -561,11 +561,17 @@
         mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
     }
 
-    private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+    private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
             throws Exception {
         PackageInfo packageInfo = packageInfoWithPermissions(permissions, PARTITION_SYSTEM);
         when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
         when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
+        return packageInfo;
+    }
+
+    private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+            throws Exception {
+        PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
         mObserver.onPackageAdded(packageName, uid);
         return packageInfo;
     }
@@ -616,14 +622,13 @@
     }
 
     @Test
-    public void testPackageUpdate() throws Exception {
+    public void testPackageRemoveThenAdd() throws Exception {
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
         mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
                 | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
-        // Remove and install the same package to simulate the update action
         when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
         mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
         mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
@@ -633,6 +638,20 @@
     }
 
     @Test
+    public void testPackageUpdate() throws Exception {
+        final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+        addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1});
+
+        // When updating a package, the broadcast receiver gets two broadcasts (a remove and then an
+        // add), but the observer sees only one callback (an update).
+        setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
+        mObserver.onPackageChanged(MOCK_PACKAGE1, MOCK_UID1);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
+    }
+
+    @Test
     public void testPackageUninstallWithMultiplePackages() throws Exception {
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 7966ba2..8a43bb4 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -169,17 +169,12 @@
   aapt::text::Printer printer(&fout);
 
   aapt::StdErrDiagnostics diagnostics;
-  auto main_command = new aapt::MainCommand(&printer, &diagnostics);
+  aapt::MainCommand main_command(&printer, &diagnostics);
 
   // Add the daemon subcommand here so it cannot be called while executing the daemon
-  main_command->AddOptionalSubcommand(
+  main_command.AddOptionalSubcommand(
       aapt::util::make_unique<aapt::DaemonCommand>(&fout, &diagnostics));
-  return main_command->Execute(args, &std::cerr);
-}
-
-// TODO(b/141312058) stop leaks
-extern "C" const char *__asan_default_options() {
-    return "detect_leaks=0";
+  return main_command.Execute(args, &std::cerr);
 }
 
 int main(int argc, char** argv) {
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index fa2b41a..ad5bb9e 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -132,11 +132,11 @@
         // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION.
         //
         // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
         //
         // To regenerate run:
         // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
-        //
-        // CHECKSTYLE:OFF Generated code
+        /
         """
 
         if (FeatureFlag.CONST_DEFS()) generateConstDefs()
diff --git a/tools/protologtool/README.md b/tools/protologtool/README.md
index 3439357..ba63957 100644
--- a/tools/protologtool/README.md
+++ b/tools/protologtool/README.md
@@ -8,8 +8,13 @@
 
 ### Code transformation
 
-Command: `process <protolog class path> <protolog implementation class path>
- <protolog groups class path> <config.jar> [<input.java>] <output.srcjar>`
+Command: `protologtool transform-protolog-calls 
+    --protolog-class <protolog class name> 
+    --protolog-impl-class <protolog implementation class name>
+    --loggroups-class <protolog groups class name>
+    --loggroups-jar <config jar path>
+    --output-srcjar <output.srcjar>
+    [<input.java>]`
 
 In this mode ProtoLogTool transforms every ProtoLog logging call in form of:
 ```java
@@ -17,16 +22,20 @@
 ```
 into:
 ```java
-if (GROUP_NAME.isLogToAny()) {
-    ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, "Format string %d %s or null", value1, value2);
+if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
+    int protoLogParam0 = value1;
+    String protoLogParam1 = String.valueOf(value2);
+    ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, 0b0100, "Format string %d %s or null", protoLogParam0, protoLogParam1);
 }
 ```
 where `ProtoLog`, `ProtoLogImpl` and `ProtoLogGroup` are the classes provided as arguments
  (can be imported, static imported or full path, wildcard imports are not allowed) and, `x` is the
  logging method. The transformation is done on the source level. A hash is generated from the format
- string and log level and inserted after the `ProtoLogGroup` argument. The format string is replaced
+ string, log level and log group name and inserted after the `ProtoLogGroup` argument. After the hash
+ we insert a bitmask specifying the types of logged parameters. The format string is replaced
  by `null` if `ProtoLogGroup.GROUP_NAME.isLogToLogcat()` returns false. If `ProtoLogGroup.GROUP_NAME.isEnabled()`
- returns false the log statement is removed entirely from the resultant code.
+ returns false the log statement is removed entirely from the resultant code. The real generated code is inlined
+ and a number of new line characters is added as to preserve line numbering in file.
 
 Input is provided as a list of java source file names. Transformed source is saved to a single
 source jar file. The ProtoLogGroup class with all dependencies should be provided as a compiled
@@ -34,8 +43,12 @@
 
 ### Viewer config generation
 
-Command: `viewerconf <protolog class path> <protolog implementation class path
-<protolog groups class path> <config.jar> [<input.java>] <output.json>`
+Command: `generate-viewer-config
+    --protolog-class <protolog class name> 
+    --loggroups-class <protolog groups class name>
+    --loggroups-jar <config jar path>
+    --viewer-conf <viewer.json>
+    [<input.java>]`
 
 This command is similar in it's syntax to the previous one, only instead of creating a processed source jar
 it writes a viewer configuration file with following schema:
@@ -46,8 +59,9 @@
     "123456": {
       "message": "Format string %d %s",
       "level": "ERROR",
-      "group": "GROUP_NAME"
-    },
+      "group": "GROUP_NAME",
+      "at": "com\/android\/server\/example\/Class.java"
+    }
   },
   "groups": {
     "GROUP_NAME": {
@@ -60,13 +74,13 @@
 
 ### Binary log viewing
 
-Command: `read <viewer.json> <wm_log.pb>`
+Command: `read-log --viewer-conf <viewer.json> <wm_log.pb>`
 
 Reads the binary ProtoLog log file and outputs a human-readable LogCat-like text log.
 
 ## What is ProtoLog?
 
-ProtoLog is a logging system created for the WindowManager project. It allows both binary and text logging
+ProtoLog is a generic logging system created for the WindowManager project. It allows both binary and text logging
 and is tunable in runtime. It consists of 3 different submodules:
 * logging system built-in the Android app,
 * log viewer for reading binary logs,
@@ -94,8 +108,7 @@
 
 To add a new logging statement just add a new call to ProtoLog.x where x is a log level.
 
-After doing any changes to logging groups or statements you should run `make update-protolog` to update
-viewer configuration saved in the code repository.
+After doing any changes to logging groups or statements you should build the project and follow instructions printed by the tool.
 
 ## How to change settings on device in runtime?
 Use the `adb shell su root cmd window logging` command. To get help just type
diff --git a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
index cb29508..a52c804 100644
--- a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
@@ -20,7 +20,6 @@
 import com.github.javaparser.ast.ImportDeclaration
 import com.github.javaparser.ast.expr.BinaryExpr
 import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.MethodCallExpr
 import com.github.javaparser.ast.expr.StringLiteralExpr
 
 object CodeUtils {
@@ -33,9 +32,14 @@
                 .map { c -> c.toInt() }.reduce { h, c -> h * 31 + c }
     }
 
-    fun isWildcardStaticImported(code: CompilationUnit, className: String): Boolean {
-        return code.findAll(ImportDeclaration::class.java)
-                .any { im -> im.isStatic && im.isAsterisk && im.name.toString() == className }
+    fun checkWildcardStaticImported(code: CompilationUnit, className: String, fileName: String) {
+        code.findAll(ImportDeclaration::class.java)
+                .forEach { im ->
+                    if (im.isStatic && im.isAsterisk && im.name.toString() == className) {
+                        throw IllegalImportException("Wildcard static imports of $className " +
+                                "methods are not supported.", ParsingContext(fileName, im))
+                    }
+                }
     }
 
     fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
@@ -59,25 +63,19 @@
                 .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
     }
 
-    fun concatMultilineString(expr: Expression): String {
+    fun concatMultilineString(expr: Expression, context: ParsingContext): String {
         return when (expr) {
             is StringLiteralExpr -> expr.asString()
             is BinaryExpr -> when {
                 expr.operator == BinaryExpr.Operator.PLUS ->
-                    concatMultilineString(expr.left) + concatMultilineString(expr.right)
+                    concatMultilineString(expr.left, context) +
+                            concatMultilineString(expr.right, context)
                 else -> throw InvalidProtoLogCallException(
-                        "messageString must be a string literal " +
-                                "or concatenation of string literals.", expr)
+                        "expected a string literal " +
+                                "or concatenation of string literals, got: $expr", context)
             }
-            else -> throw InvalidProtoLogCallException("messageString must be a string literal " +
-                    "or concatenation of string literals.", expr)
-        }
-    }
-
-    fun getPositionString(call: MethodCallExpr, fileName: String): String {
-        return when {
-            call.range.isPresent -> "$fileName:${call.range.get().begin.line}"
-            else -> fileName
+            else -> throw InvalidProtoLogCallException("expected a string literal " +
+                    "or concatenation of string literals, got: $expr", context)
         }
     }
 }
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
index 7759f35..e88f0f8 100644
--- a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
@@ -22,7 +22,7 @@
     DEBUG, VERBOSE, INFO, WARN, ERROR, WTF;
 
     companion object {
-        fun getLevelForMethodName(name: String, node: Node): LogLevel {
+        fun getLevelForMethodName(name: String, node: Node, context: ParsingContext): LogLevel {
             return when (name) {
                 "d" -> DEBUG
                 "v" -> VERBOSE
@@ -30,7 +30,8 @@
                 "w" -> WARN
                 "e" -> ERROR
                 "wtf" -> WTF
-                else -> throw InvalidProtoLogCallException("Unknown log level $name", node)
+                else ->
+                    throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
             }
         }
     }
diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt
similarity index 60%
copy from core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
copy to tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt
index 10d0551..c6aedfc 100644
--- a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java
+++ b/tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,13 +12,15 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package com.android.frameworks.coretests;
+package com.android.protolog.tool
 
-import android.app.Activity;
+import com.github.javaparser.ast.Node
 
-public class TestActivity extends Activity {
+data class ParsingContext(val filePath: String, val lineNumber: Int) {
+    constructor(filePath: String, node: Node)
+            : this(filePath, if (node.range.isPresent) node.range.get().begin.line else -1)
 
+    constructor() : this("", -1)
 }
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
index eae6396..2181cf6 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -38,23 +38,27 @@
     private fun getLogGroupName(
         expr: Expression,
         isClassImported: Boolean,
-        staticImports: Set<String>
+        staticImports: Set<String>,
+        fileName: String
     ): String {
+        val context = ParsingContext(fileName, expr)
         return when (expr) {
             is NameExpr -> when {
                 expr.nameAsString in staticImports -> expr.nameAsString
                 else ->
-                    throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr)
+                    throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+                            context)
             }
             is FieldAccessExpr -> when {
                 expr.scope.toString() == protoLogGroupClassName
                         || isClassImported &&
                         expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString
                 else ->
-                    throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr)
+                    throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+                            context)
             }
             else -> throw InvalidProtoLogCallException("Invalid group argument " +
-                    "- must be ProtoLogGroup enum member reference", expr)
+                    "- must be ProtoLogGroup enum member reference: $expr", context)
         }
     }
 
@@ -69,12 +73,10 @@
                 !call.scope.isPresent && staticLogImports.contains(call.name.toString())
     }
 
-    open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?): CompilationUnit {
-        if (CodeUtils.isWildcardStaticImported(code, protoLogClassName) ||
-                CodeUtils.isWildcardStaticImported(code, protoLogGroupClassName)) {
-            throw IllegalImportException("Wildcard static imports of $protoLogClassName " +
-                    "and $protoLogGroupClassName methods are not supported.")
-        }
+    open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?, fileName: String):
+            CompilationUnit {
+        CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
+        CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)
 
         val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName)
         val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName)
@@ -86,22 +88,25 @@
                 .filter { call ->
                     isProtoCall(call, isLogClassImported, staticLogImports)
                 }.forEach { call ->
+                    val context = ParsingContext(fileName, call)
                     if (call.arguments.size < 2) {
                         throw InvalidProtoLogCallException("Method signature does not match " +
-                                "any ProtoLog method.", call)
+                                "any ProtoLog method: $call", context)
                     }
 
-                    val messageString = CodeUtils.concatMultilineString(call.getArgument(1))
+                    val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
+                            context)
                     val groupNameArg = call.getArgument(0)
                     val groupName =
-                            getLogGroupName(groupNameArg, isGroupClassImported, staticGroupImports)
+                            getLogGroupName(groupNameArg, isGroupClassImported,
+                                    staticGroupImports, fileName)
                     if (groupName !in groupMap) {
                         throw InvalidProtoLogCallException("Unknown group argument " +
-                                "- not a ProtoLogGroup enum member", call)
+                                "- not a ProtoLogGroup enum member: $call", context)
                     }
 
                     callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName(
-                            call.name.toString(), call), groupMap.getValue(groupName))
+                            call.name.toString(), call, context), groupMap.getValue(groupName))
                 }
         return code
     }
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 53834a6..97f3de2 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -17,7 +17,9 @@
 package com.android.protolog.tool
 
 import com.android.protolog.tool.CommandOptions.Companion.USAGE
+import com.github.javaparser.ParseProblemException
 import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.CompilationUnit
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileOutputStream
@@ -48,7 +50,7 @@
         command.javaSourceArgs.forEach { path ->
             val file = File(path)
             val text = file.readText()
-            val code = StaticJavaParser.parse(text)
+            val code = tryParse(text, path)
             val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
                     .get().nameAsString else ""
             val newPath = pack.replace('.', '/') + '/' + file.name
@@ -66,6 +68,19 @@
         out.close()
     }
 
+    private fun tryParse(code: String, fileName: String): CompilationUnit {
+        try {
+            return StaticJavaParser.parse(code)
+        } catch (ex: ParseProblemException) {
+            val problem = ex.problems.first()
+            throw ParsingException("Java parsing erro" +
+                    "r: ${problem.verboseMessage}",
+                    ParsingContext(fileName, problem.location.orElse(null)
+                            ?.begin?.range?.orElse(null)?.begin?.line
+                            ?: 0))
+        }
+    }
+
     private fun viewerConf(command: CommandOptions) {
         val groups = ProtoLogGroupReader()
                 .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
@@ -76,7 +91,7 @@
             val file = File(path)
             val text = file.readText()
             if (containsProtoLogText(text, command.protoLogClassNameArg)) {
-                val code = StaticJavaParser.parse(text)
+                val code = tryParse(text, path)
                 val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
                         .get().nameAsString else ""
                 val newPath = pack.replace('.', '/') + '/' + file.name
@@ -104,8 +119,11 @@
                 CommandOptions.READ_LOG_CMD -> read(command)
             }
         } catch (ex: InvalidCommandException) {
-            println(ex.message)
+            println("\n${ex.message}\n")
             showHelpAndExit()
+        } catch (ex: CodeProcessingException) {
+            println("\n${ex.message}\n")
+            exitProcess(1)
         }
     }
 }
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 3f38bc0..00fd038 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -73,8 +73,7 @@
         }
         val ifStmt: IfStmt
         if (group.enabled) {
-            val position = CodeUtils.getPositionString(call, fileName)
-            val hash = CodeUtils.hash(position, messageString, level, group)
+            val hash = CodeUtils.hash(fileName, messageString, level, group)
             val newCall = call.clone()
             if (!group.textEnabled) {
                 // Remove message string if text logging is not enabled by default.
@@ -99,7 +98,8 @@
                 NodeList<Expression>(newCall.arguments[0].clone()))
             if (argTypes.size != call.arguments.size - 2) {
                 throw InvalidProtoLogCallException(
-                        "Number of arguments does not mach format string", call)
+                        "Number of arguments (${argTypes.size} does not mach format" +
+                                " string in: $call", ParsingContext(fileName, call))
             }
             val blockStmt = BlockStmt()
             if (argTypes.isNotEmpty()) {
@@ -225,7 +225,7 @@
         processedCode = code.split('\n').toMutableList()
         offsets = IntArray(processedCode.size)
         LexicalPreservingPrinter.setup(compilationUnit)
-        protoLogCallProcessor.process(compilationUnit, this)
+        protoLogCallProcessor.process(compilationUnit, this, fileName)
         // return LexicalPreservingPrinter.print(compilationUnit)
         return processedCode.joinToString("\n")
     }
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
index 4c41797..941455a 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
@@ -32,13 +32,14 @@
         group: LogGroup
     ) {
         if (group.enabled) {
-            val position = CodeUtils.getPositionString(call, fileName)
+            val position = fileName
             val key = CodeUtils.hash(position, messageString, level, group)
             if (statements.containsKey(key)) {
                 if (statements[key] != LogCall(messageString, level, group, position)) {
                     throw HashCollisionException(
                             "Please modify the log message \"$messageString\" " +
-                                    "or \"${statements[key]}\" - their hashes are equal.")
+                                    "or \"${statements[key]}\" - their hashes are equal.",
+                            ParsingContext(fileName, call))
                 }
             } else {
                 groups.add(group)
@@ -54,7 +55,7 @@
 
     fun processClass(unit: CompilationUnit, fileName: String) {
         this.fileName = fileName
-        protoLogCallVisitor.process(unit, this)
+        protoLogCallVisitor.process(unit, this, fileName)
     }
 
     fun build(): String {
diff --git a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
index 0401d8f..ae00df1 100644
--- a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
@@ -16,16 +16,23 @@
 
 package com.android.protolog.tool
 
-import com.github.javaparser.ast.Node
 import java.lang.Exception
-import java.lang.RuntimeException
 
-class HashCollisionException(message: String) : RuntimeException(message)
+open class CodeProcessingException(message: String, context: ParsingContext)
+    : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
+        "  $message")
 
-class IllegalImportException(message: String) : Exception(message)
+class HashCollisionException(message: String, context: ParsingContext) :
+        CodeProcessingException(message, context)
 
-class InvalidProtoLogCallException(message: String, node: Node)
-    : RuntimeException("$message\nAt: $node")
+class IllegalImportException(message: String, context: ParsingContext) :
+        CodeProcessingException("Illegal import: $message", context)
+
+class InvalidProtoLogCallException(message: String, context: ParsingContext)
+    : CodeProcessingException("InvalidProtoLogCall: $message", context)
+
+class ParsingException(message: String, context: ParsingContext)
+    : CodeProcessingException(message, context)
 
 class InvalidViewerConfigException(message: String) : Exception(message)
 
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
index 0acbc90..b916f8f 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
@@ -55,40 +55,40 @@
                 LogLevel.DEBUG, LogGroup("test2", true, true, "TAG")))
     }
 
-    @Test
-    fun isWildcardStaticImported_true() {
+    @Test(expected = IllegalImportException::class)
+    fun checkWildcardStaticImported_true() {
         val code = """package org.example.test;
             import static org.example.Test.*;
         """
-        assertTrue(CodeUtils.isWildcardStaticImported(
-                StaticJavaParser.parse(code), "org.example.Test"))
+        CodeUtils.checkWildcardStaticImported(
+                StaticJavaParser.parse(code), "org.example.Test", "")
     }
 
     @Test
-    fun isWildcardStaticImported_notStatic() {
+    fun checkWildcardStaticImported_notStatic() {
         val code = """package org.example.test;
             import org.example.Test.*;
         """
-        assertFalse(CodeUtils.isWildcardStaticImported(
-                StaticJavaParser.parse(code), "org.example.Test"))
+        CodeUtils.checkWildcardStaticImported(
+                StaticJavaParser.parse(code), "org.example.Test", "")
     }
 
     @Test
-    fun isWildcardStaticImported_differentClass() {
+    fun checkWildcardStaticImported_differentClass() {
         val code = """package org.example.test;
             import static org.example.Test2.*;
         """
-        assertFalse(CodeUtils.isWildcardStaticImported(
-                StaticJavaParser.parse(code), "org.example.Test"))
+        CodeUtils.checkWildcardStaticImported(
+                StaticJavaParser.parse(code), "org.example.Test", "")
     }
 
     @Test
-    fun isWildcardStaticImported_notWildcard() {
+    fun checkWildcardStaticImported_notWildcard() {
         val code = """package org.example.test;
             import org.example.Test.test;
         """
-        assertFalse(CodeUtils.isWildcardStaticImported(
-                StaticJavaParser.parse(code), "org.example.Test"))
+        CodeUtils.checkWildcardStaticImported(
+                StaticJavaParser.parse(code), "org.example.Test", "")
     }
 
     @Test
@@ -156,7 +156,7 @@
     @Test
     fun concatMultilineString_single() {
         val str = StringLiteralExpr("test")
-        val out = CodeUtils.concatMultilineString(str)
+        val out = CodeUtils.concatMultilineString(str, ParsingContext())
         assertEquals("test", out)
     }
 
@@ -166,7 +166,7 @@
             "test" + "abc"
         """
         val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
-        val out = CodeUtils.concatMultilineString(code)
+        val out = CodeUtils.concatMultilineString(code, ParsingContext())
         assertEquals("testabc", out)
     }
 
@@ -176,7 +176,7 @@
             "test" + "abc" + "1234" + "test"
         """
         val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
-        val out = CodeUtils.concatMultilineString(code)
+        val out = CodeUtils.concatMultilineString(code, ParsingContext())
         assertEquals("testabc1234test", out)
     }
 }
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
index d20ce7e..97f67a0 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
@@ -66,7 +66,7 @@
         """
         groupMap["TEST"] = LogGroup("TEST", true, false, "WindowManager")
         groupMap["ERROR"] = LogGroup("ERROR", true, true, "WindowManagerERROR")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
         assertEquals(2, calls.size)
         var c = calls[0]
         assertEquals("test %b", c.messageString)
@@ -93,7 +93,7 @@
             }
         """
         groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
         checkCalls()
     }
 
@@ -112,7 +112,7 @@
             }
         """
         groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
         checkCalls()
     }
 
@@ -130,7 +130,7 @@
             }
         """
         groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
     }
 
     @Test
@@ -147,7 +147,7 @@
             }
         """
         groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
         assertEquals(0, calls.size)
     }
 
@@ -162,7 +162,7 @@
                 }
             }
         """
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
     }
 
     @Test(expected = InvalidProtoLogCallException::class)
@@ -176,7 +176,7 @@
                 }
             }
         """
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
     }
 
     @Test(expected = InvalidProtoLogCallException::class)
@@ -190,7 +190,7 @@
                 }
             }
         """
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
     }
 
     @Test(expected = InvalidProtoLogCallException::class)
@@ -204,7 +204,7 @@
                 }
             }
         """
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
     }
 
     @Test
@@ -220,7 +220,7 @@
             }
         """
         groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager")
-        visitor.process(StaticJavaParser.parse(code), processor)
+        visitor.process(StaticJavaParser.parse(code), processor, "")
         checkCalls()
     }
 }
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index f221fbd..e746300 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -78,7 +78,7 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1922613844, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
                 }
             }
             """.trimIndent()
@@ -88,7 +88,7 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 805272208, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2); 
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2); 
             
             }
                 }
@@ -100,8 +100,8 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1922613844, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1922613844, 9, "test %d %f", protoLogParam0, protoLogParam1); }
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -154595499, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
                 }
             }
             """.trimIndent()
@@ -111,7 +111,7 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { org.example.ProtoLogImpl.w(TEST_GROUP, 1913810354, 0, "test", (Object[]) null); }
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { org.example.ProtoLogImpl.w(TEST_GROUP, -1741986185, 0, "test", (Object[]) null); }
                 }
             }
             """.trimIndent()
@@ -121,7 +121,7 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1922613844, 9, null, protoLogParam0, protoLogParam1); }
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, null, protoLogParam0, protoLogParam1); }
                 }
             }
             """.trimIndent()
@@ -131,7 +131,7 @@
 
             class Test {
                 void test() {
-                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 805272208, 9, null, protoLogParam0, protoLogParam1, protoLogParam2); 
+                    if (org.example.ProtoLogImpl.isEnabled(TEST_GROUP)) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, null, protoLogParam0, protoLogParam1, protoLogParam2); 
             
             }
                 }
@@ -175,7 +175,8 @@
         var code = StaticJavaParser.parse(TEST_CODE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
@@ -199,7 +200,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(6, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("1922613844", methodCall.arguments[1].toString())
+        assertEquals("1698911065", methodCall.arguments[1].toString())
         assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
         assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
         assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -212,7 +213,8 @@
         var code = StaticJavaParser.parse(TEST_CODE_MULTICALLS)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             val calls = code.findAll(MethodCallExpr::class.java)
@@ -241,7 +243,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(6, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("1922613844", methodCall.arguments[1].toString())
+        assertEquals("1698911065", methodCall.arguments[1].toString())
         assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
         assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
         assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -254,7 +256,8 @@
         var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
@@ -279,7 +282,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(7, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("805272208", methodCall.arguments[1].toString())
+        assertEquals("1780316587", methodCall.arguments[1].toString())
         assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
         assertEquals("protoLogParam0", methodCall.arguments[4].toString())
         assertEquals("protoLogParam1", methodCall.arguments[5].toString())
@@ -292,7 +295,8 @@
         var code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
@@ -316,7 +320,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(5, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("1913810354", methodCall.arguments[1].toString())
+        assertEquals("-1741986185", methodCall.arguments[1].toString())
         assertEquals(0.toString(), methodCall.arguments[2].toString())
         assertEquals(TRANSFORMED_CODE_NO_PARAMS, out)
     }
@@ -326,7 +330,8 @@
         var code = StaticJavaParser.parse(TEST_CODE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
@@ -350,7 +355,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(6, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("1922613844", methodCall.arguments[1].toString())
+        assertEquals("1698911065", methodCall.arguments[1].toString())
         assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
         assertEquals("null", methodCall.arguments[3].toString())
         assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -363,7 +368,8 @@
         var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
@@ -388,7 +394,7 @@
         assertEquals("w", methodCall.name.asString())
         assertEquals(7, methodCall.arguments.size)
         assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
-        assertEquals("805272208", methodCall.arguments[1].toString())
+        assertEquals("1780316587", methodCall.arguments[1].toString())
         assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
         assertEquals("null", methodCall.arguments[3].toString())
         assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -402,7 +408,8 @@
         var code = StaticJavaParser.parse(TEST_CODE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
@@ -426,7 +433,8 @@
         var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
 
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
index d3f8c76..2b6abcd 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
@@ -50,7 +50,8 @@
     @Test
     fun processClass() {
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
@@ -78,7 +79,8 @@
     @Test
     fun processClass_nonUnique() {
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
@@ -102,7 +104,8 @@
     @Test
     fun processClass_disabled() {
         Mockito.`when`(processor.process(any(CompilationUnit::class.java),
-                any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
+                any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+                .thenAnswer { invocation ->
             val visitor = invocation.arguments[1] as ProtoLogCallVisitor
 
             visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index eb5a717..2afb14a 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1056,26 +1056,6 @@
     }
 
     /**
-     * @hide
-     * Returns Randomized MAC address to use with the network.
-     * If it is not set/valid, creates a new randomized address.
-     * If it can't generate a valid mac, returns the default MAC.
-     */
-    public @NonNull MacAddress getOrCreateRandomizedMacAddress() {
-        int randomMacGenerationCount = 0;
-        while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
-                && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
-            mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
-            randomMacGenerationCount++;
-        }
-
-        if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
-            mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
-        }
-        return mRandomizedMacAddress;
-    }
-
-    /**
      * Returns MAC address set to be the local randomized MAC address.
      * Depending on user preference, the device may or may not use the returned MAC address for
      * connections to this network.
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index ba9fc78..6d7e621 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
@@ -62,7 +61,8 @@
         config.updateIdentifier = "1234";
         config.fromWifiNetworkSpecifier = true;
         config.fromWifiNetworkSuggestion = true;
-        MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
+        config.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress());
+        MacAddress macBeforeParcel = config.getRandomizedMacAddress();
         Parcel parcelW = Parcel.obtain();
         config.writeToParcel(parcelW, 0);
         byte[] bytes = parcelW.marshall();
@@ -75,7 +75,7 @@
 
         // lacking a useful config.equals, check two fields near the end.
         assertEquals(cookie, reconfig.getMoTree());
-        assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+        assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress());
         assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
         assertFalse(reconfig.trusted);
         assertTrue(config.fromWifiNetworkSpecifier);
@@ -193,19 +193,6 @@
     }
 
     @Test
-    public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
-        WifiConfiguration config = new WifiConfiguration();
-        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
-        assertEquals(defaultMac, config.getRandomizedMacAddress());
-
-        MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
-        MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
-
-        assertNotEquals(defaultMac, firstMacAddress);
-        assertEquals(firstMacAddress, secondMacAddress);
-    }
-
-    @Test
     public void testSetRandomizedMacAddress_ChangesSavedAddress() {
         WifiConfiguration config = new WifiConfiguration();
         MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
@@ -219,36 +206,6 @@
     }
 
     @Test
-    public void testGetOrCreateRandomizedMacAddress_ReRandomizesInvalidAddress() {
-        WifiConfiguration config =  new WifiConfiguration();
-
-        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
-        MacAddress macAddressZeroes = MacAddress.ALL_ZEROS_ADDRESS;
-        MacAddress macAddressMulticast = MacAddress.fromString("03:ff:ff:ff:ff:ff");
-        MacAddress macAddressGlobal = MacAddress.fromString("fc:ff:ff:ff:ff:ff");
-
-        config.setRandomizedMacAddress(null);
-        MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertNotEquals(macAfterChange, null);
-
-        config.setRandomizedMacAddress(defaultMac);
-        macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertNotEquals(macAfterChange, defaultMac);
-
-        config.setRandomizedMacAddress(macAddressZeroes);
-        macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertNotEquals(macAfterChange, macAddressZeroes);
-
-        config.setRandomizedMacAddress(macAddressMulticast);
-        macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertNotEquals(macAfterChange, macAddressMulticast);
-
-        config.setRandomizedMacAddress(macAddressGlobal);
-        macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertNotEquals(macAfterChange, macAddressGlobal);
-    }
-
-    @Test
     public void testSetRandomizedMacAddress_DoesNothingWhenNull() {
         WifiConfiguration config = new WifiConfiguration();
         MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);