Merge "Update ActivityEmbedding Property Javadoc"
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index d401373..aeb6abc 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -306,6 +306,10 @@
         return eventId & MASK_TYPE;
     }
 
+    static boolean isReward(int eventId) {
+        return getEventType(eventId) == TYPE_REWARD;
+    }
+
     @NonNull
     static String eventToString(int eventId) {
         switch (eventId & MASK_TYPE) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index 2e2a9b5..e91ed12 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -17,15 +17,19 @@
 package com.android.server.tare;
 
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.util.TimeUtils.dumpTime;
 
 import static com.android.server.tare.TareUtils.cakeToString;
-import static com.android.server.tare.TareUtils.dumpTime;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.IndentingPrintWriter;
 import android.util.SparseLongArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -34,6 +38,21 @@
  * Ledger to track the last recorded balance and recent activities of an app.
  */
 class Ledger {
+    /** The window size within which rewards will be counted and used towards reward limiting. */
+    private static final long TOTAL_REWARD_WINDOW_MS = 24 * HOUR_IN_MILLIS;
+    /** The number of buckets to split {@link #TOTAL_REWARD_WINDOW_MS} into. */
+    @VisibleForTesting
+    static final int NUM_REWARD_BUCKET_WINDOWS = 4;
+    /**
+     * The duration size of each bucket resulting from splitting {@link #TOTAL_REWARD_WINDOW_MS}
+     * into smaller buckets.
+     */
+    private static final long REWARD_BUCKET_WINDOW_SIZE_MS =
+            TOTAL_REWARD_WINDOW_MS / NUM_REWARD_BUCKET_WINDOWS;
+    /** The maximum number of transactions to retain in memory at any one time. */
+    @VisibleForTesting
+    static final int MAX_TRANSACTION_COUNT = 50;
+
     static class Transaction {
         public final long startTimeMs;
         public final long endTimeMs;
@@ -54,18 +73,47 @@
         }
     }
 
+    static class RewardBucket {
+        @CurrentTimeMillisLong
+        public long startTimeMs;
+        public final SparseLongArray cumulativeDelta = new SparseLongArray();
+
+        private void reset() {
+            startTimeMs = 0;
+            cumulativeDelta.clear();
+        }
+    }
+
     /** Last saved balance. This doesn't take currently ongoing events into account. */
     private long mCurrentBalance = 0;
-    private final List<Transaction> mTransactions = new ArrayList<>();
-    private final SparseLongArray mCumulativeDeltaPerReason = new SparseLongArray();
-    private long mEarliestSumTime;
+    private final Transaction[] mTransactions = new Transaction[MAX_TRANSACTION_COUNT];
+    /** Index within {@link #mTransactions} where the next transaction should be placed. */
+    private int mTransactionIndex = 0;
+    private final RewardBucket[] mRewardBuckets = new RewardBucket[NUM_REWARD_BUCKET_WINDOWS];
+    /** Index within {@link #mRewardBuckets} of the current active bucket. */
+    private int mRewardBucketIndex = 0;
 
     Ledger() {
     }
 
-    Ledger(long currentBalance, @NonNull List<Transaction> transactions) {
+    Ledger(long currentBalance, @NonNull List<Transaction> transactions,
+            @NonNull List<RewardBucket> rewardBuckets) {
         mCurrentBalance = currentBalance;
-        mTransactions.addAll(transactions);
+
+        final int numTxs = transactions.size();
+        for (int i = Math.max(0, numTxs - MAX_TRANSACTION_COUNT); i < numTxs; ++i) {
+            mTransactions[mTransactionIndex++] = transactions.get(i);
+        }
+        mTransactionIndex %= MAX_TRANSACTION_COUNT;
+
+        final int numBuckets = rewardBuckets.size();
+        if (numBuckets > 0) {
+            // Set the index to -1 so that we put the first bucket in index 0.
+            mRewardBucketIndex = -1;
+            for (int i = Math.max(0, numBuckets - NUM_REWARD_BUCKET_WINDOWS); i < numBuckets; ++i) {
+                mRewardBuckets[++mRewardBucketIndex] = rewardBuckets.get(i);
+            }
+        }
     }
 
     long getCurrentBalance() {
@@ -74,66 +122,142 @@
 
     @Nullable
     Transaction getEarliestTransaction() {
-        if (mTransactions.size() > 0) {
-            return mTransactions.get(0);
+        for (int t = 0; t < mTransactions.length; ++t) {
+            final Transaction transaction =
+                    mTransactions[(mTransactionIndex + t) % mTransactions.length];
+            if (transaction != null) {
+                return transaction;
+            }
         }
         return null;
     }
 
     @NonNull
+    List<RewardBucket> getRewardBuckets() {
+        final long cutoffMs = getCurrentTimeMillis() - TOTAL_REWARD_WINDOW_MS;
+        final List<RewardBucket> list = new ArrayList<>(NUM_REWARD_BUCKET_WINDOWS);
+        for (int i = 1; i <= NUM_REWARD_BUCKET_WINDOWS; ++i) {
+            final int idx = (mRewardBucketIndex + i) % NUM_REWARD_BUCKET_WINDOWS;
+            final RewardBucket rewardBucket = mRewardBuckets[idx];
+            if (rewardBucket != null) {
+                if (cutoffMs <= rewardBucket.startTimeMs) {
+                    list.add(rewardBucket);
+                } else {
+                    rewardBucket.reset();
+                }
+            }
+        }
+        return list;
+    }
+
+    @NonNull
     List<Transaction> getTransactions() {
-        return mTransactions;
+        final List<Transaction> list = new ArrayList<>(MAX_TRANSACTION_COUNT);
+        for (int i = 0; i < MAX_TRANSACTION_COUNT; ++i) {
+            final int idx = (mTransactionIndex + i) % MAX_TRANSACTION_COUNT;
+            final Transaction transaction = mTransactions[idx];
+            if (transaction != null) {
+                list.add(transaction);
+            }
+        }
+        return list;
     }
 
     void recordTransaction(@NonNull Transaction transaction) {
-        mTransactions.add(transaction);
+        mTransactions[mTransactionIndex] = transaction;
         mCurrentBalance += transaction.delta;
+        mTransactionIndex = (mTransactionIndex + 1) % MAX_TRANSACTION_COUNT;
 
-        final long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
-        mCumulativeDeltaPerReason.put(transaction.eventId, sum + transaction.delta);
-        mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs);
+        if (EconomicPolicy.isReward(transaction.eventId)) {
+            final RewardBucket bucket = getCurrentRewardBucket();
+            bucket.cumulativeDelta.put(transaction.eventId,
+                    bucket.cumulativeDelta.get(transaction.eventId, 0) + transaction.delta);
+        }
+    }
+
+    @NonNull
+    private RewardBucket getCurrentRewardBucket() {
+        RewardBucket bucket = mRewardBuckets[mRewardBucketIndex];
+        final long now = getCurrentTimeMillis();
+        if (bucket == null) {
+            bucket = new RewardBucket();
+            bucket.startTimeMs = now;
+            mRewardBuckets[mRewardBucketIndex] = bucket;
+            return bucket;
+        }
+
+        if (now - bucket.startTimeMs < REWARD_BUCKET_WINDOW_SIZE_MS) {
+            return bucket;
+        }
+
+        mRewardBucketIndex = (mRewardBucketIndex + 1) % NUM_REWARD_BUCKET_WINDOWS;
+        bucket = mRewardBuckets[mRewardBucketIndex];
+        if (bucket == null) {
+            bucket = new RewardBucket();
+            mRewardBuckets[mRewardBucketIndex] = bucket;
+        }
+        bucket.reset();
+        // Using now as the start time means there will be some gaps between sequential buckets,
+        // but makes processing of large gaps between events easier.
+        bucket.startTimeMs = now;
+        return bucket;
     }
 
     long get24HourSum(int eventId, final long now) {
         final long windowStartTime = now - 24 * HOUR_IN_MILLIS;
-        if (mEarliestSumTime < windowStartTime) {
-            // Need to redo sums
-            mCumulativeDeltaPerReason.clear();
-            for (int i = mTransactions.size() - 1; i >= 0; --i) {
-                final Transaction transaction = mTransactions.get(i);
-                if (transaction.endTimeMs <= windowStartTime) {
-                    break;
-                }
-                long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
-                if (transaction.startTimeMs >= windowStartTime) {
-                    sum += transaction.delta;
-                } else {
-                    // Pro-rate durationed deltas. Intentionally floor the result.
-                    sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime)
-                            * transaction.delta)
-                            / (transaction.endTimeMs - transaction.startTimeMs);
-                }
-                mCumulativeDeltaPerReason.put(transaction.eventId, sum);
+        long sum = 0;
+        for (int i = 0; i < mRewardBuckets.length; ++i) {
+            final RewardBucket bucket = mRewardBuckets[i];
+            if (bucket != null
+                    && bucket.startTimeMs >= windowStartTime && bucket.startTimeMs < now) {
+                sum += bucket.cumulativeDelta.get(eventId, 0);
             }
-            mEarliestSumTime = windowStartTime;
         }
-        return mCumulativeDeltaPerReason.get(eventId);
+        return sum;
     }
 
-    /** Deletes transactions that are older than {@code minAgeMs}. */
-    void removeOldTransactions(long minAgeMs) {
+    /**
+     * Deletes transactions that are older than {@code minAgeMs}.
+     * @return The earliest transaction in the ledger, or {@code null} if there are no more
+     * transactions.
+     */
+    @Nullable
+    Transaction removeOldTransactions(long minAgeMs) {
         final long cutoff = getCurrentTimeMillis() - minAgeMs;
-        while (mTransactions.size() > 0 && mTransactions.get(0).endTimeMs <= cutoff) {
-            mTransactions.remove(0);
+        for (int t = 0; t < mTransactions.length; ++t) {
+            final int idx = (mTransactionIndex + t) % mTransactions.length;
+            final Transaction transaction = mTransactions[idx];
+            if (transaction == null) {
+                continue;
+            }
+            if (transaction.endTimeMs <= cutoff) {
+                mTransactions[idx] = null;
+            } else {
+                // Everything we look at after this transaction will also be within the window,
+                // so no need to go further.
+                return transaction;
+            }
         }
+        return null;
     }
 
     void dump(IndentingPrintWriter pw, int numRecentTransactions) {
         pw.print("Current balance", cakeToString(getCurrentBalance())).println();
+        pw.println();
 
-        final int size = mTransactions.size();
-        for (int i = Math.max(0, size - numRecentTransactions); i < size; ++i) {
-            final Transaction transaction = mTransactions.get(i);
+        boolean printedTransactionTitle = false;
+        for (int t = 0; t < Math.min(MAX_TRANSACTION_COUNT, numRecentTransactions); ++t) {
+            final int idx = (mTransactionIndex - t + MAX_TRANSACTION_COUNT) % MAX_TRANSACTION_COUNT;
+            final Transaction transaction = mTransactions[idx];
+            if (transaction == null) {
+                continue;
+            }
+
+            if (!printedTransactionTitle) {
+                pw.println("Transactions:");
+                pw.increaseIndent();
+                printedTransactionTitle = true;
+            }
 
             dumpTime(pw, transaction.startTimeMs);
             pw.print("--");
@@ -151,5 +275,42 @@
             pw.print(cakeToString(transaction.ctp));
             pw.println(")");
         }
+        if (printedTransactionTitle) {
+            pw.decreaseIndent();
+            pw.println();
+        }
+
+        final long now = getCurrentTimeMillis();
+        boolean printedBucketTitle = false;
+        for (int b = 0; b < NUM_REWARD_BUCKET_WINDOWS; ++b) {
+            final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS)
+                    % NUM_REWARD_BUCKET_WINDOWS;
+            final RewardBucket rewardBucket = mRewardBuckets[idx];
+            if (rewardBucket == null) {
+                continue;
+            }
+
+            if (!printedBucketTitle) {
+                pw.println("Reward buckets:");
+                pw.increaseIndent();
+                printedBucketTitle = true;
+            }
+
+            dumpTime(pw, rewardBucket.startTimeMs);
+            pw.print(" (");
+            TimeUtils.formatDuration(now - rewardBucket.startTimeMs, pw);
+            pw.println(" ago):");
+            pw.increaseIndent();
+            for (int r = 0; r < rewardBucket.cumulativeDelta.size(); ++r) {
+                pw.print(EconomicPolicy.eventToString(rewardBucket.cumulativeDelta.keyAt(r)));
+                pw.print(": ");
+                pw.println(cakeToString(rewardBucket.cumulativeDelta.valueAt(r)));
+            }
+            pw.decreaseIndent();
+        }
+        if (printedBucketTitle) {
+            pw.decreaseIndent();
+            pw.println();
+        }
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 941cc39..8f7657e 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -62,15 +62,14 @@
     private static final int MAX_NUM_TRANSACTION_DUMP = 25;
     /**
      * The maximum amount of time we'll keep a transaction around for.
-     * For now, only keep transactions we actually have a use for. We can increase it if we want
-     * to use older transactions or provide older transactions to apps.
      */
-    private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
+    private static final long MAX_TRANSACTION_AGE_MS = 8 * 24 * HOUR_IN_MILLIS;
 
     private static final String XML_TAG_HIGH_LEVEL_STATE = "irs-state";
     private static final String XML_TAG_LEDGER = "ledger";
     private static final String XML_TAG_TARE = "tare";
     private static final String XML_TAG_TRANSACTION = "transaction";
+    private static final String XML_TAG_REWARD_BUCKET = "rewardBucket";
     private static final String XML_TAG_USER = "user";
     private static final String XML_TAG_PERIOD_REPORT = "report";
 
@@ -346,8 +345,8 @@
                 for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) {
                     final String pkgName = mLedgers.keyAt(uIdx, pIdx);
                     final Ledger ledger = mLedgers.get(userId, pkgName);
-                    ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
-                    Ledger.Transaction transaction = ledger.getEarliestTransaction();
+                    final Ledger.Transaction transaction =
+                            ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
                     if (transaction != null) {
                         earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs);
                     }
@@ -370,6 +369,7 @@
         final String pkgName;
         final long curBalance;
         final List<Ledger.Transaction> transactions = new ArrayList<>();
+        final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>();
 
         pkgName = parser.getAttributeValue(null, XML_ATTR_PACKAGE_NAME);
         curBalance = parser.getAttributeLong(null, XML_ATTR_CURRENT_BALANCE);
@@ -391,8 +391,7 @@
                 }
                 continue;
             }
-            if (eventType != XmlPullParser.START_TAG || !XML_TAG_TRANSACTION.equals(tagName)) {
-                // Expecting only "transaction" tags.
+            if (eventType != XmlPullParser.START_TAG || tagName == null) {
                 Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName);
                 return null;
             }
@@ -402,25 +401,37 @@
             if (DEBUG) {
                 Slog.d(TAG, "Starting ledger tag: " + tagName);
             }
-            final String tag = parser.getAttributeValue(null, XML_ATTR_TAG);
-            final long startTime = parser.getAttributeLong(null, XML_ATTR_START_TIME);
-            final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME);
-            final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
-            final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
-            final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP);
-            if (endTime <= endTimeCutoff) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Skipping event because it's too old.");
-                }
-                continue;
+            switch (tagName) {
+                case XML_TAG_TRANSACTION:
+                    final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME);
+                    if (endTime <= endTimeCutoff) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Skipping event because it's too old.");
+                        }
+                        continue;
+                    }
+                    final String tag = parser.getAttributeValue(null, XML_ATTR_TAG);
+                    final long startTime = parser.getAttributeLong(null, XML_ATTR_START_TIME);
+                    final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
+                    final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
+                    final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP);
+                    transactions.add(
+                            new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp));
+                    break;
+                case XML_TAG_REWARD_BUCKET:
+                    rewardBuckets.add(readRewardBucketFromXml(parser));
+                    break;
+                default:
+                    // Expecting only "transaction" and "rewardBucket" tags.
+                    Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName);
+                    return null;
             }
-            transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp));
         }
 
         if (!isInstalled) {
             return null;
         }
-        return Pair.create(pkgName, new Ledger(curBalance, transactions));
+        return Pair.create(pkgName, new Ledger(curBalance, transactions, rewardBuckets));
     }
 
     /**
@@ -508,6 +519,44 @@
         return report;
     }
 
+    /**
+     * @param parser Xml parser at the beginning of a {@value #XML_TAG_REWARD_BUCKET} tag. The next
+     *               "parser.next()" call will take the parser into the body of the tag.
+     * @return Newly instantiated {@link Ledger.RewardBucket} holding all the information we just
+     * read out of the xml tag.
+     */
+    @Nullable
+    private static Ledger.RewardBucket readRewardBucketFromXml(TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket();
+
+        rewardBucket.startTimeMs = parser.getAttributeLong(null, XML_ATTR_START_TIME);
+
+        for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT;
+                eventType = parser.next()) {
+            final String tagName = parser.getName();
+            if (eventType == XmlPullParser.END_TAG) {
+                if (XML_TAG_REWARD_BUCKET.equals(tagName)) {
+                    // We've reached the end of the rewardBucket tag.
+                    break;
+                }
+                continue;
+            }
+            if (eventType != XmlPullParser.START_TAG || !XML_ATTR_DELTA.equals(tagName)) {
+                // Expecting only delta tags.
+                Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName);
+                return null;
+            }
+
+            final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID);
+            final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA);
+            rewardBucket.cumulativeDelta.put(eventId, delta);
+        }
+
+        return rewardBucket;
+    }
+
     private void scheduleCleanup(long earliestEndTime) {
         if (earliestEndTime == Long.MAX_VALUE) {
             return;
@@ -595,6 +644,11 @@
                 }
                 writeTransaction(out, transaction);
             }
+
+            final List<Ledger.RewardBucket> rewardBuckets = ledger.getRewardBuckets();
+            for (int r = 0; r < rewardBuckets.size(); ++r) {
+                writeRewardBucket(out, rewardBuckets.get(r));
+            }
             out.endTag(null, XML_TAG_LEDGER);
         }
         out.endTag(null, XML_TAG_USER);
@@ -616,6 +670,23 @@
         out.endTag(null, XML_TAG_TRANSACTION);
     }
 
+    private static void writeRewardBucket(@NonNull TypedXmlSerializer out,
+            @NonNull Ledger.RewardBucket rewardBucket) throws IOException {
+        final int numEvents = rewardBucket.cumulativeDelta.size();
+        if (numEvents == 0) {
+            return;
+        }
+        out.startTag(null, XML_TAG_REWARD_BUCKET);
+        out.attributeLong(null, XML_ATTR_START_TIME, rewardBucket.startTimeMs);
+        for (int i = 0; i < numEvents; ++i) {
+            out.startTag(null, XML_ATTR_DELTA);
+            out.attributeInt(null, XML_ATTR_EVENT_ID, rewardBucket.cumulativeDelta.keyAt(i));
+            out.attributeLong(null, XML_ATTR_DELTA, rewardBucket.cumulativeDelta.valueAt(i));
+            out.endTag(null, XML_ATTR_DELTA);
+        }
+        out.endTag(null, XML_TAG_REWARD_BUCKET);
+    }
+
     private static void writeReport(@NonNull TypedXmlSerializer out,
             @NonNull Analyst.Report report) throws IOException {
         out.startTag(null, XML_TAG_PERIOD_REPORT);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
index 6b6984f..aa4c75a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -19,26 +19,15 @@
 import static android.app.tare.EconomyManager.CAKE_IN_ARC;
 
 import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.text.SimpleDateFormat;
 import java.time.Clock;
 
 class TareUtils {
-    @SuppressLint("SimpleDateFormat")
-    private static final SimpleDateFormat sDumpDateFormat =
-            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-
     @VisibleForTesting
     static Clock sSystemClock = Clock.systemUTC();
 
-    static void dumpTime(IndentingPrintWriter pw, long time) {
-        pw.print(sDumpDateFormat.format(time));
-    }
-
     static long getCurrentTimeMillis() {
         return sSystemClock.millis();
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a83cbd..b383d7d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -374,6 +374,7 @@
     int mCurDefaultDisplayDpi;
     @UnsupportedAppUsage
     boolean mDensityCompatMode;
+    private CompatibilityInfo mCompatibilityInfo;
     @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R,
             publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.")
     Configuration mConfiguration;
@@ -612,7 +613,7 @@
         }
 
         public ActivityClientRecord(IBinder token, Intent intent, int ident,
-                ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
+                ActivityInfo info, Configuration overrideConfig,
                 String referrer, IVoiceInteractor voiceInteractor, Bundle state,
                 PersistableBundle persistentState, List<ResultInfo> pendingResults,
                 List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
@@ -627,7 +628,6 @@
             this.referrer = referrer;
             this.voiceInteractor = voiceInteractor;
             this.activityInfo = info;
-            this.compatInfo = compatInfo;
             this.state = state;
             this.persistentState = persistentState;
             this.pendingResults = pendingResults;
@@ -635,8 +635,7 @@
             this.isForward = isForward;
             this.profilerInfo = profilerInfo;
             this.overrideConfig = overrideConfig;
-            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
-                    compatInfo);
+            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo);
             mActivityOptions = activityOptions;
             mLaunchedFromBubble = launchedFromBubble;
             mInitialTaskFragmentToken = initialTaskFragmentToken;
@@ -804,7 +803,6 @@
 
     static final class CreateBackupAgentData {
         ApplicationInfo appInfo;
-        CompatibilityInfo compatInfo;
         int backupMode;
         int userId;
         int operationType;
@@ -1038,15 +1036,13 @@
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     sync, false, mAppThread.asBinder(), sendingUser);
             r.info = info;
-            r.compatInfo = compatInfo;
             sendMessage(H.RECEIVER, r);
         }
 
         public final void scheduleCreateBackupAgent(ApplicationInfo app,
-                CompatibilityInfo compatInfo, int backupMode, int userId, int operationType) {
+                int backupMode, int userId, int operationType) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
-            d.compatInfo = compatInfo;
             d.backupMode = backupMode;
             d.userId = userId;
             d.operationType = operationType;
@@ -1054,11 +1050,9 @@
             sendMessage(H.CREATE_BACKUP_AGENT, d);
         }
 
-        public final void scheduleDestroyBackupAgent(ApplicationInfo app,
-                CompatibilityInfo compatInfo, int userId) {
+        public final void scheduleDestroyBackupAgent(ApplicationInfo app, int userId) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
-            d.compatInfo = compatInfo;
             d.userId = userId;
 
             sendMessage(H.DESTROY_BACKUP_AGENT, d);
@@ -1070,7 +1064,6 @@
             CreateServiceData s = new CreateServiceData();
             s.token = token;
             s.info = info;
-            s.compatInfo = compatInfo;
 
             sendMessage(H.CREATE_SERVICE, s);
         }
@@ -2577,16 +2570,16 @@
                 registerPackage);
     }
 
-    @Override
     @UnsupportedAppUsage
     public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
         return getPackageInfo(ai, compatInfo, null, false, true, false);
     }
 
-    private LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo,
-            boolean isSdkSandbox) {
-        return getPackageInfo(ai, compatInfo, null, false, true, false, isSdkSandbox);
+    @Override
+    public LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {
+        return getPackageInfo(ai, mCompatibilityInfo, null /* baseLoader */,
+                false /* securityViolation */, true /* includeCode */, false /* registerPackage */);
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -3538,7 +3531,7 @@
     private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
         ActivityInfo aInfo = r.activityInfo;
         if (r.packageInfo == null) {
-            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
+            r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -4279,8 +4272,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo, data.compatInfo);
+        final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
 
         IActivityManager mgr = ActivityManager.getService();
 
@@ -4366,7 +4358,7 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
         String packageName = packageInfo.mPackageName;
         if (packageName == null) {
             Slog.d(TAG, "Asked to create backup agent for nonexistent package");
@@ -4439,7 +4431,7 @@
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
         String packageName = packageInfo.mPackageName;
         ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId);
         BackupAgent agent = backupAgents.get(packageName);
@@ -4471,8 +4463,7 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo, data.compatInfo);
+        final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
         Service service = null;
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
@@ -5306,6 +5297,7 @@
     }
 
     private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
+        mCompatibilityInfo = data.info;
         LoadedApk apk = peekPackageInfo(data.pkg, false);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
@@ -6486,6 +6478,7 @@
         mConfigurationController.setConfiguration(data.config);
         mConfigurationController.setCompatConfiguration(data.config);
         mConfiguration = mConfigurationController.getConfiguration();
+        mCompatibilityInfo = data.compatInfo;
 
         mProfiler = new Profiler();
         String agent = null;
@@ -6569,7 +6562,9 @@
         }
 
         final boolean isSdkSandbox = data.sdkSandboxClientAppPackage != null;
-        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo, isSdkSandbox);
+        data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */,
+                false /* securityViolation */, true /* includeCode */,
+                false /* registerPackage */, isSdkSandbox);
         if (isSdkSandbox) {
             data.info.setSdkSandboxStorage(data.sdkSandboxClientAppVolumeUuid,
                     data.sdkSandboxClientAppPackage);
@@ -6844,7 +6839,7 @@
     private void handleInstrumentWithoutRestart(AppBindData data) {
         try {
             data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
-            data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+            data.info = getPackageInfoNoCheck(data.appInfo);
             mInstrumentingWithoutRestart = true;
             final InstrumentationInfo ii = prepareInstrumentation(data);
             final ContextImpl appContext =
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 65e6ab7..389da2d 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -23,7 +23,6 @@
 import android.app.servertransaction.TransactionExecutor;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.util.MergedConfiguration;
@@ -180,8 +179,7 @@
             PendingTransactionActions pendingActions, ActivityOptions activityOptions);
 
     /** Get package info. */
-    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
-            CompatibilityInfo compatInfo);
+    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai);
 
     /** Deliver app configuration change notification. */
     public abstract void handleConfigurationChanged(Configuration config);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 75ad363..843b684 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -102,10 +102,9 @@
     void scheduleLowMemory();
     void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
     void setSchedulingGroup(int group);
-    void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo,
+    void scheduleCreateBackupAgent(in ApplicationInfo app,
             int backupMode, int userId, int operationType);
-    void scheduleDestroyBackupAgent(in ApplicationInfo app,
-            in CompatibilityInfo compatInfo, int userId);
+    void scheduleDestroyBackupAgent(in ApplicationInfo app, int userId);
     void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 076dbef..03d6e27 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -30,7 +30,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.BaseBundle;
 import android.os.Bundle;
@@ -58,7 +57,6 @@
     private ActivityInfo mInfo;
     private Configuration mCurConfig;
     private Configuration mOverrideConfig;
-    private CompatibilityInfo mCompatInfo;
     private String mReferrer;
     private IVoiceInteractor mVoiceInteractor;
     private int mProcState;
@@ -94,7 +92,7 @@
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
         ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
-                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+                mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
                 mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
                 client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
                 mTaskFragmentToken);
@@ -115,7 +113,7 @@
 
     /** Obtain an instance initialized with provided params. */
     public static LaunchActivityItem obtain(Intent intent, int ident, ActivityInfo info,
-            Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
+            Configuration curConfig, Configuration overrideConfig,
             String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
             List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
@@ -126,7 +124,7 @@
         if (instance == null) {
             instance = new LaunchActivityItem();
         }
-        setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
+        setValues(instance, intent, ident, info, curConfig, overrideConfig, referrer,
                 voiceInteractor, procState, state, persistentState, pendingResults,
                 pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
                 activityClientController, shareableActivityToken,
@@ -137,7 +135,7 @@
 
     @Override
     public void recycle() {
-        setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
+        setValues(this, null, 0, null, null, null, null, null, 0, null, null, null, null,
                 null, false, null, null, null, null, false, null);
         ObjectPool.recycle(this);
     }
@@ -153,7 +151,6 @@
         dest.writeTypedObject(mInfo, flags);
         dest.writeTypedObject(mCurConfig, flags);
         dest.writeTypedObject(mOverrideConfig, flags);
-        dest.writeTypedObject(mCompatInfo, flags);
         dest.writeString(mReferrer);
         dest.writeStrongInterface(mVoiceInteractor);
         dest.writeInt(mProcState);
@@ -175,8 +172,7 @@
     private LaunchActivityItem(Parcel in) {
         setValues(this, in.readTypedObject(Intent.CREATOR), in.readInt(),
                 in.readTypedObject(ActivityInfo.CREATOR), in.readTypedObject(Configuration.CREATOR),
-                in.readTypedObject(Configuration.CREATOR),
-                in.readTypedObject(CompatibilityInfo.CREATOR), in.readString(),
+                in.readTypedObject(Configuration.CREATOR), in.readString(),
                 IVoiceInteractor.Stub.asInterface(in.readStrongBinder()), in.readInt(),
                 in.readBundle(getClass().getClassLoader()),
                 in.readPersistableBundle(getClass().getClassLoader()),
@@ -216,7 +212,6 @@
         return intentsEqual && mIdent == other.mIdent
                 && activityInfoEqual(other.mInfo) && Objects.equals(mCurConfig, other.mCurConfig)
                 && Objects.equals(mOverrideConfig, other.mOverrideConfig)
-                && Objects.equals(mCompatInfo, other.mCompatInfo)
                 && Objects.equals(mReferrer, other.mReferrer)
                 && mProcState == other.mProcState && areBundlesEqualRoughly(mState, other.mState)
                 && areBundlesEqualRoughly(mPersistentState, other.mPersistentState)
@@ -237,7 +232,6 @@
         result = 31 * result + mIdent;
         result = 31 * result + Objects.hashCode(mCurConfig);
         result = 31 * result + Objects.hashCode(mOverrideConfig);
-        result = 31 * result + Objects.hashCode(mCompatInfo);
         result = 31 * result + Objects.hashCode(mReferrer);
         result = 31 * result + Objects.hashCode(mProcState);
         result = 31 * result + getRoughBundleHashCode(mState);
@@ -292,7 +286,7 @@
     // Using the same method to set and clear values to make sure we don't forget anything
     private static void setValues(LaunchActivityItem instance, Intent intent, int ident,
             ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
-            CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+            String referrer, IVoiceInteractor voiceInteractor,
             int procState, Bundle state, PersistableBundle persistentState,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
             ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
@@ -303,7 +297,6 @@
         instance.mInfo = info;
         instance.mCurConfig = curConfig;
         instance.mOverrideConfig = overrideConfig;
-        instance.mCompatInfo = compatInfo;
         instance.mReferrer = referrer;
         instance.mVoiceInteractor = voiceInteractor;
         instance.mProcState = procState;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 8db298f..861a850 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1325,8 +1325,11 @@
      * flashlight brightness level via
      * {@link android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel }.
      * If this value is equal to 1, flashlight brightness control is not supported.
-     * The value for this key will be null for devices with no flash unit.
-     * This level must be set to a safe value to prevent any burn out issues.</p>
+     * The value for this key will be null for devices with no flash unit.</p>
+     * <p>The maximum value is guaranteed to be safe to use for an indefinite duration in
+     * terms of device flashlight lifespan, but may be too bright for comfort for many
+     * use cases. Use the default torch brightness value to avoid problems with an
+     * over-bright flashlight.</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      */
     @PublicKey
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 91d6a9b..40f7533 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -117,6 +117,7 @@
     /**
      * Shuts down the service
      */
+    @EnforcePermission("SHUTDOWN")
     void shutdown();
 
     /**
@@ -277,6 +278,7 @@
      */
     void setUidOnMeteredNetworkDenylist(int uid, boolean enable);
     void setUidOnMeteredNetworkAllowlist(int uid, boolean enable);
+    @EnforcePermission("NETWORK_SETTINGS")
     boolean setDataSaverModeEnabled(boolean enable);
 
     void setUidCleartextNetworkPolicy(int uid, int policy);
@@ -308,5 +310,6 @@
     void removeInterfaceFromLocalNetwork(String iface);
     int removeRoutesFromLocalNetwork(in List<RouteInfo> routes);
 
+    @EnforcePermission("OBSERVE_NETWORK_POLICY")
     boolean isNetworkRestricted(int uid);
 }
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 5dae313..c692981 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -145,5 +145,8 @@
      *
      * @hide
      */
-    SurfaceSyncGroup.SyncTarget getSyncTarget();
+    @Nullable
+    default SurfaceSyncGroup.SyncTarget getSyncTarget() {
+        return null;
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 9c63d56..121839b 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -666,14 +666,12 @@
 
     /**
      * Sort the list of InputMethodSubtype
-     * @param context Context will be used for getting localized strings from IME
-     * @param flags Flags for the sort order
      * @param imi InputMethodInfo of which subtypes are subject to be sorted
      * @param subtypeList List of InputMethodSubtype which will be sorted
      * @return Sorted list of subtypes
      * @hide
      */
-    public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi,
+    public static List<InputMethodSubtype> sort(InputMethodInfo imi,
             List<InputMethodSubtype> subtypeList) {
         if (imi == null) return subtypeList;
         final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>(
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 5672697..4248096 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -186,7 +186,11 @@
         if (viewRoot == null) {
             return false;
         }
-        return addToSync(viewRoot.getSyncTarget());
+        SyncTarget syncTarget = viewRoot.getSyncTarget();
+        if (syncTarget == null) {
+            return false;
+        }
+        return addToSync(syncTarget);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
index a2eca3a..84b2a35 100644
--- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -21,7 +21,9 @@
 import com.android.internal.app.procstats.ProcessStats;
 
 interface IProcessStats {
+    @EnforcePermission("PACKAGE_USAGE_STATS")
     byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
+    @EnforcePermission("PACKAGE_USAGE_STATS")
     ParcelFileDescriptor getStatsOverTime(long minTime);
     int getCurrentMemoryState();
 
@@ -43,6 +45,7 @@
      * @param List of Files of individual commits in protobuf binary or one that is merged from them.
      * @param ProcessStats object that will be used to return the full set of merged stats.
      */
+     @EnforcePermission("PACKAGE_USAGE_STATS")
      long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
         out List<ParcelFileDescriptor> committedStats, out ProcessStats mergedStats);
 
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 50639be..0a5a4d5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -32,7 +32,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
@@ -140,7 +139,6 @@
         activityInfo.name = "name";
         Configuration overrideConfig = new Configuration();
         overrideConfig.assetsSeq = 5;
-        CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
         String referrer = "referrer";
         int procState = 4;
         Bundle bundle = new Bundle();
@@ -152,7 +150,7 @@
 
         Supplier<LaunchActivityItem> itemSupplier = () -> new LaunchActivityItemBuilder()
                 .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
-                .setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer)
+                .setOverrideConfig(overrideConfig).setReferrer(referrer)
                 .setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
                 .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
                 .setIsForward(true).setAssistToken(assistToken)
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 26d9628..2cd890c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -23,7 +23,6 @@
 import android.app.ResultInfo;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -96,7 +95,6 @@
         private ActivityInfo mInfo;
         private Configuration mCurConfig;
         private Configuration mOverrideConfig;
-        private CompatibilityInfo mCompatInfo;
         private String mReferrer;
         private IVoiceInteractor mVoiceInteractor;
         private int mProcState;
@@ -137,11 +135,6 @@
             return this;
         }
 
-        LaunchActivityItemBuilder setCompatInfo(CompatibilityInfo compatInfo) {
-            mCompatInfo = compatInfo;
-            return this;
-        }
-
         LaunchActivityItemBuilder setReferrer(String referrer) {
             mReferrer = referrer;
             return this;
@@ -214,7 +207,7 @@
 
         LaunchActivityItem build() {
             return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
-                    mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
+                    mCurConfig, mOverrideConfig, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
                     mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
                     null /* activityClientController */, mShareableActivityToken,
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 0eca0a8..e9bbdbe 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -187,7 +187,6 @@
         activityInfo.name = "name";
         Configuration overrideConfig = new Configuration();
         overrideConfig.assetsSeq = 5;
-        CompatibilityInfo compat = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
         String referrer = "referrer";
         int procState = 4;
         Bundle bundle = new Bundle();
@@ -198,7 +197,7 @@
 
         LaunchActivityItem item = new LaunchActivityItemBuilder()
                 .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
-                .setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer)
+                .setOverrideConfig(overrideConfig).setReferrer(referrer)
                 .setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
                 .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic())
                 .setPendingNewIntents(referrerIntentList()).setIsForward(true)
@@ -489,13 +488,13 @@
 
         @Override
         public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo,
-                CompatibilityInfo compatibilityInfo, int i, int userId, int operatioType)
+                int i, int userId, int operatioType)
                 throws RemoteException {
         }
 
         @Override
         public void scheduleDestroyBackupAgent(ApplicationInfo applicationInfo,
-                CompatibilityInfo compatibilityInfo, int userId) throws RemoteException {
+                int userId) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 47f70dd..f4a6f02 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -54,7 +54,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.UserHandle;
@@ -339,8 +338,7 @@
             doNothing().when(packageInfo).updateApplicationInfo(any(), any());
 
             return new ActivityClientRecord(mock(IBinder.class), Intent.makeMainActivity(component),
-                    0 /* ident */, info, new Configuration(),
-                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */,
+                    0 /* ident */, info, new Configuration(), null /* referrer */,
                     null /* voiceInteractor */, null /* state */, null /* persistentState */,
                     null /* pendingResults */, null /* pendingNewIntents */,
                     null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index c14c3c5..5ab21bc 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -237,6 +237,7 @@
     private final boolean mUnlockedDeviceRequired;
     private final boolean mIsStrongBoxBacked;
     private final int mMaxUsageCount;
+    private final boolean mRollbackResistant;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -259,7 +260,8 @@
             boolean userConfirmationRequired,
             boolean unlockedDeviceRequired,
             boolean isStrongBoxBacked,
-            int maxUsageCount) {
+            int maxUsageCount,
+            boolean rollbackResistant) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -283,6 +285,7 @@
         mUnlockedDeviceRequired = unlockedDeviceRequired;
         mIsStrongBoxBacked = isStrongBoxBacked;
         mMaxUsageCount = maxUsageCount;
+        mRollbackResistant = rollbackResistant;
     }
 
     /**
@@ -563,6 +566,17 @@
     }
 
     /**
+     * Returns {@code true} if the key is rollback-resistant, meaning that when deleted it is
+     * guaranteed to be permanently deleted and unusable.
+     *
+     * @see Builder#setRollbackResistant(boolean)
+     * @hide
+     */
+    public boolean isRollbackResistant() {
+        return mRollbackResistant;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -591,6 +605,7 @@
         private boolean mIsStrongBoxBacked = false;
         private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
         private String mAttestKeyAlias = null;
+        private boolean mRollbackResistant = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1097,6 +1112,21 @@
         }
 
         /**
+         * Sets whether the key should be rollback-resistant, meaning that when deleted it is
+         * guaranteed to be permanently deleted and unusable.  Not all implementations support
+         * rollback-resistant keys.  This method is hidden because implementations only support a
+         * limited number of rollback-resistant keys; currently the available space is reserved for
+         * critical system keys.
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setRollbackResistant(boolean rollbackResistant) {
+            mRollbackResistant = rollbackResistant;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -1124,7 +1154,8 @@
                     mUserConfirmationRequired,
                     mUnlockedDeviceRequired,
                     mIsStrongBoxBacked,
-                    mMaxUsageCount);
+                    mMaxUsageCount,
+                    mRollbackResistant);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 33411e1..dfda356 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -783,6 +783,12 @@
                         params.getMaxUsageCount()
                 ));
             }
+
+            if (params.isRollbackResistant()) {
+                importArgs.add(KeyStore2ParameterUtils.makeBool(
+                        KeymasterDefs.KM_TAG_ROLLBACK_RESISTANT
+                ));
+            }
         } catch (IllegalArgumentException | IllegalStateException e) {
             throw new KeyStoreException(e);
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 0972cf2..1636c5f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -25,6 +25,7 @@
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertNotNull
 import junit.framework.Assert.assertTrue
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,6 +62,12 @@
         bubbles.put(1, user1Bubbles)
     }
 
+    @After
+    fun teardown() {
+        // Clean up the any persisted bubbles for the next run
+        repository.persistsToDisk(SparseArray())
+    }
+
     @Test
     fun testReadWriteOperation() {
         // Verify read before write doesn't cause FileNotFoundException
@@ -71,4 +78,4 @@
         repository.persistsToDisk(bubbles)
         assertTrue(sparseArraysEqual(bubbles, repository.readFromDisk()))
     }
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 24c1179..44c0b54 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -166,9 +166,8 @@
      */
     public void registerScanRequest() {
         if (mScanRequestCount.getAndIncrement() == 0) {
-            Client client = getOrCreateClient();
             try {
-                mMediaRouterService.startScan(client);
+                mMediaRouterService.startScan(getOrCreateClient());
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -194,9 +193,8 @@
                     }
                 })
                 == 0) {
-            Client client = getOrCreateClient();
             try {
-                mMediaRouterService.stopScan(client);
+                mMediaRouterService.stopScan(getOrCreateClient());
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -425,15 +423,11 @@
      */
     @NonNull
     public List<RoutingSessionInfo> getRemoteSessions() {
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                return mMediaRouterService.getRemoteSessions(client);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            return mMediaRouterService.getRemoteSessions(getOrCreateClient());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return Collections.emptyList();
     }
 
     /**
@@ -516,14 +510,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.setRouteVolumeWithManager(
+                    getOrCreateClient(), requestId, route, volume);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -545,15 +537,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.setSessionVolumeWithManager(
-                        client, requestId, sessionInfo.getId(), volume);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.setSessionVolumeWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), volume);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -810,15 +799,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.selectRouteWithManager(
-                        client, requestId, sessionInfo.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.selectRouteWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -852,15 +838,12 @@
             return;
         }
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.deselectRouteWithManager(
-                        client, requestId, sessionInfo.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.deselectRouteWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -877,15 +860,12 @@
     public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
         Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                int requestId = mNextRequestId.getAndIncrement();
-                mMediaRouterService.releaseSessionWithManager(
-                        client, requestId, sessionInfo.getId());
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            int requestId = mNextRequestId.getAndIncrement();
+            mMediaRouterService.releaseSessionWithManager(
+                    getOrCreateClient(), requestId, sessionInfo.getId());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -898,14 +878,11 @@
             @NonNull MediaRoute2Info route) {
         int requestId = createTransferRequest(session, route);
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                mMediaRouterService.transferToRouteWithManager(
-                        client, requestId, session.getId(), route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            mMediaRouterService.transferToRouteWithManager(
+                    getOrCreateClient(), requestId, session.getId(), route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -918,14 +895,11 @@
 
         int requestId = createTransferRequest(oldSession, route);
 
-        Client client = getOrCreateClient();
-        if (client != null) {
-            try {
-                mMediaRouterService.requestCreateSessionWithManager(
-                        client, requestId, oldSession, route);
-            } catch (RemoteException ex) {
-                throw ex.rethrowFromSystemServer();
-            }
+        try {
+            mMediaRouterService.requestCreateSessionWithManager(
+                    getOrCreateClient(), requestId, oldSession, route);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 281501e..f4355c3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -84,7 +84,8 @@
     private InfoMediaManager mInfoMediaManager;
     private String mPackageName;
     private MediaDevice mOnTransferBluetoothDevice;
-    private AudioManager mAudioManager;
+    @VisibleForTesting
+    AudioManager mAudioManager;
 
     @VisibleForTesting
     List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index b416738..1a08366 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,8 +44,6 @@
     private final Handler mReceiverHandler;
     private final MobileTelephonyCallback mTelephonyCallback;
 
-    private boolean mListening = false;
-
     /**
      * MobileStatusTracker constructors
      *
@@ -78,7 +76,6 @@
      * Config the MobileStatusTracker to start or stop monitoring platform signals.
      */
     public void setListening(boolean listening) {
-        mListening = listening;
         if (listening) {
             mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
         } else {
@@ -86,10 +83,6 @@
         }
     }
 
-    public boolean isListening() {
-        return mListening;
-    }
-
     private void updateDataSim() {
         int activeDataSubId = mDefaults.getActiveDataSubId();
         if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 4332bd2..45c0d78 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -57,6 +57,7 @@
     private final Handler mHandler;
     private final Handler mMainThreadHandler;
     private final Set<Integer> mNetworks = new HashSet<>();
+    private int mPrimaryNetworkId;
     // Save the previous HISTORY_SIZE states for logging.
     private final String[] mHistory = new String[HISTORY_SIZE];
     // Where to copy the next state into.
@@ -106,6 +107,7 @@
             if (!mNetworks.contains(network.getNetId())) {
                 mNetworks.add(network.getNetId());
             }
+            mPrimaryNetworkId = network.getNetId();
             updateWifiInfo(wifiInfo);
             updateStatusLabel();
             mMainThreadHandler.post(() -> postResults());
@@ -121,10 +123,13 @@
             recordLastWifiNetwork(log);
             if (mNetworks.contains(network.getNetId())) {
                 mNetworks.remove(network.getNetId());
-                updateWifiInfo(null);
-                updateStatusLabel();
-                mMainThreadHandler.post(() -> postResults());
             }
+            if (network.getNetId() != mPrimaryNetworkId) {
+                return;
+            }
+            updateWifiInfo(null);
+            updateStatusLabel();
+            mMainThreadHandler.post(() -> postResults());
         }
     };
     private final NetworkCallback mDefaultNetworkCallback =
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 8e850b2..24bb1bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.media;
 
-import static android.bluetooth.BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES;
 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -29,10 +28,12 @@
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.media.RoutingSessionInfo;
@@ -71,6 +72,7 @@
     private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id";
     private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
     private static final String TEST_SESSION_ID = "session_id";
+    private static final String TEST_ADDRESS = "00:01:02:03:04:05";
 
     @Mock
     private InfoMediaManager mInfoMediaManager;
@@ -90,6 +92,8 @@
     private MediaRoute2Info mRouteInfo1;
     @Mock
     private MediaRoute2Info mRouteInfo2;
+    @Mock
+    private AudioManager mAudioManager;
 
     private Context mContext;
     private LocalMediaManager mLocalMediaManager;
@@ -118,6 +122,7 @@
                 TEST_PACKAGE_NAME);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
                 mInfoMediaManager, "com.test.packagename");
+        mLocalMediaManager.mAudioManager = mAudioManager;
     }
 
     @Test
@@ -551,16 +556,12 @@
     }
 
     @Test
-    public void onDeviceListAdded_haveDisconnectedDevice_addDisconnectedDevice() {
+    public void onDeviceListAdded_haveMutingExpectedDevice_addMutingExpectedDevice() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
-        final MediaDevice device2 = mock(MediaDevice.class);
-        final MediaDevice device3 = mock(MediaDevice.class);
         mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
-        devices.add(device2);
-        mLocalMediaManager.mMediaDevices.add(device3);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+        devices.add(mLocalMediaManager.mPhoneDevice);
 
         final List<LocalBluetoothProfile> profiles = new ArrayList<>();
         final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
@@ -573,22 +574,25 @@
         bluetoothDevices.add(bluetoothDevice);
         mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
 
+        AudioDeviceAttributes audioDeviceAttributes = new AudioDeviceAttributes(
+                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, TEST_ADDRESS);
+
+        when(mAudioManager.getMutingExpectedDevice()).thenReturn(audioDeviceAttributes);
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager);
         when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
         when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(cachedDevice.isConnected()).thenReturn(false);
         when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
         when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+        when(cachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
         when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice);
         when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
 
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
-        when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
-        when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
         when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
         when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
@@ -634,68 +638,6 @@
     }
 
     @Test
-    public void onDeviceListAdded_haveDisconnectedDevice_list5DisconnectedDevice() {
-        final List<MediaDevice> devices = new ArrayList<>();
-        final MediaDevice device1 = mock(MediaDevice.class);
-        final MediaDevice device2 = mock(MediaDevice.class);
-        final MediaDevice device3 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
-        devices.add(device1);
-        devices.add(device2);
-        mLocalMediaManager.mMediaDevices.add(device3);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
-
-        final List<LocalBluetoothProfile> profiles = new ArrayList<>();
-        final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
-        profiles.add(a2dpProfile);
-
-        final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
-        final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
-        final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
-        final BluetoothDevice bluetoothDevice3 = mock(BluetoothDevice.class);
-        final BluetoothDevice bluetoothDevice4 = mock(BluetoothDevice.class);
-        final BluetoothDevice bluetoothDevice5 = mock(BluetoothDevice.class);
-        final BluetoothDevice bluetoothDevice6 = mock(BluetoothDevice.class);
-        final BluetoothClass bluetoothClass = mock(BluetoothClass.class);
-        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
-        final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class);
-        bluetoothDevices.add(bluetoothDevice);
-        bluetoothDevices.add(bluetoothDevice2);
-        bluetoothDevices.add(bluetoothDevice3);
-        bluetoothDevices.add(bluetoothDevice4);
-        bluetoothDevices.add(bluetoothDevice5);
-        bluetoothDevices.add(bluetoothDevice6);
-        mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
-
-        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager);
-        when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
-        when(cachedManager.findDevice(bluetoothDevice2)).thenReturn(cachedDevice);
-        when(cachedManager.findDevice(bluetoothDevice3)).thenReturn(cachedDevice);
-        when(cachedManager.findDevice(bluetoothDevice4)).thenReturn(cachedDevice);
-        when(cachedManager.findDevice(bluetoothDevice5)).thenReturn(cachedDevice);
-        when(cachedManager.findDevice(bluetoothDevice6)).thenReturn(cachedDevice);
-        when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        when(cachedDevice.isConnected()).thenReturn(false);
-        when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
-        when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
-        when(bluetoothDevice.getBluetoothClass()).thenReturn(bluetoothClass);
-        when(bluetoothClass.getDeviceClass()).thenReturn(AUDIO_VIDEO_HEADPHONES);
-
-        when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
-        when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
-        when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
-        when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
-        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
-
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
-        mLocalMediaManager.registerCallback(mCallback);
-        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
-
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(7);
-        verify(mCallback).onDeviceListUpdate(any());
-    }
-
-    @Test
     public void onDeviceListAdded_bluetoothAdapterIsNull_noDisconnectedDeviceAdded() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
new file mode 100644
index 0000000..dc7e313d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiStatusTrackerTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.settingslib.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkScoreManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiStatusTrackerTest {
+    @Mock Context mContext;
+    @Mock WifiManager mWifiManager;
+    @Mock NetworkScoreManager mNetworkScoreManager;
+    @Mock ConnectivityManager mConnectivityManager;
+    @Mock Runnable mCallback;
+
+    private final ArgumentCaptor<ConnectivityManager.NetworkCallback>
+            mNetworkCallbackCaptor =
+            ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /**
+     * Verify that we only clear the WifiInfo if the primary network was lost.
+     */
+    @Test
+    public void testWifiInfoClearedOnPrimaryNetworkLost() {
+        WifiStatusTracker wifiStatusTracker = new WifiStatusTracker(mContext, mWifiManager,
+                mNetworkScoreManager, mConnectivityManager, mCallback);
+        wifiStatusTracker.setListening(true);
+
+        verify(mConnectivityManager)
+                .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture(), any());
+
+        // Trigger a validation callback for the primary Wifi network.
+        WifiInfo primaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(primaryWifiInfo.makeCopy(anyLong())).thenReturn(primaryWifiInfo);
+        when(primaryWifiInfo.isPrimary()).thenReturn(true);
+        int primaryRssi = -55;
+        when(primaryWifiInfo.getRssi()).thenReturn(primaryRssi);
+        NetworkCapabilities primaryCap = new NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setTransportInfo(primaryWifiInfo)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .build();
+        Network primaryNetwork = Mockito.mock(Network.class);
+        int primaryNetworkId = 1;
+        when(primaryNetwork.getNetId()).thenReturn(primaryNetworkId);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(primaryNetwork, primaryCap);
+
+        // Verify primary wifi info is the one being used.
+        assertThat(wifiStatusTracker.connected).isTrue();
+        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+
+        // Trigger a validation callback for the non-primary Wifi network.
+        WifiInfo nonPrimaryWifiInfo = Mockito.mock(WifiInfo.class);
+        when(nonPrimaryWifiInfo.makeCopy(anyLong())).thenReturn(nonPrimaryWifiInfo);
+        when(nonPrimaryWifiInfo.isPrimary()).thenReturn(false);
+        int nonPrimaryRssi = -75;
+        when(primaryWifiInfo.getRssi()).thenReturn(nonPrimaryRssi);
+        NetworkCapabilities nonPrimaryCap = new NetworkCapabilities.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .setTransportInfo(nonPrimaryWifiInfo)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+                .build();
+        Network nonPrimaryNetwork = Mockito.mock(Network.class);
+        int nonPrimaryNetworkId = 2;
+        when(nonPrimaryNetwork.getNetId()).thenReturn(nonPrimaryNetworkId);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(nonPrimaryNetwork, nonPrimaryCap);
+
+        // Verify primary wifi info is still the one being used.
+        assertThat(wifiStatusTracker.connected).isTrue();
+        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+
+        // Lose the non-primary network.
+        mNetworkCallbackCaptor.getValue().onLost(nonPrimaryNetwork);
+
+        // Verify primary wifi info is still the one being used.
+        assertThat(wifiStatusTracker.connected).isTrue();
+        assertThat(wifiStatusTracker.rssi).isEqualTo(primaryRssi);
+
+        // Lose the primary network.
+        mNetworkCallbackCaptor.getValue().onLost(primaryNetwork);
+
+        // Verify we aren't connected anymore.
+        assertThat(wifiStatusTracker.connected).isFalse();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 73bc92d2..b0f025d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -61,7 +61,7 @@
             new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
 
     public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
-            new BooleanFlag(109, false);
+            new BooleanFlag(109, false, true);
 
     public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
             new BooleanFlag(110, false, true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index eeb1010..ec0d081 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,9 +134,18 @@
         mQSCarrierGroupController
                 .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
 
-        List<String> rssiIgnoredSlots = List.of(
-                getResources().getString(com.android.internal.R.string.status_bar_mobile)
-        );
+        List<String> rssiIgnoredSlots;
+
+        if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+            rssiIgnoredSlots = List.of(
+                    getResources().getString(com.android.internal.R.string.status_bar_no_calling),
+                    getResources().getString(com.android.internal.R.string.status_bar_call_strength)
+            );
+        } else {
+            rssiIgnoredSlots = List.of(
+                    getResources().getString(com.android.internal.R.string.status_bar_mobile)
+            );
+        }
 
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
                 mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index e925b54..2dac639 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,6 +27,7 @@
     @JvmField val contentDescription: String? = null,
     @JvmField val typeContentDescription: String? = null,
     @JvmField val roaming: Boolean = false,
+    @JvmField val providerModelBehavior: Boolean = false
 ) {
     /**
      * Changes the visibility of this state by returning a copy with the visibility changed.
@@ -40,4 +41,4 @@
         if (this.visible == visible) return this
         else return copy(visible = visible)
     }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 703b95a..592da65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@
     private View mSpacer;
     @Nullable
     private CellSignalState mLastSignalState;
-    private boolean mMobileSignalInitialized = false;
+    private boolean mProviderModelInitialized = false;
     private boolean mIsSingleCarrier;
 
     public QSCarrier(Context context) {
@@ -96,25 +96,35 @@
             mMobileRoaming.setImageTintList(colorStateList);
             mMobileSignal.setImageTintList(colorStateList);
 
-            if (!mMobileSignalInitialized) {
-                mMobileSignalInitialized = true;
-                mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
+            if (state.providerModelBehavior) {
+                if (!mProviderModelInitialized) {
+                    mProviderModelInitialized = true;
+                    mMobileSignal.setImageDrawable(
+                            mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
+                }
+                mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
+                mMobileSignal.setContentDescription(state.contentDescription);
+            } else {
+                if (!mProviderModelInitialized) {
+                    mProviderModelInitialized = true;
+                    mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
+                }
+                mMobileSignal.setImageLevel(state.mobileSignalIconId);
+                StringBuilder contentDescription = new StringBuilder();
+                if (state.contentDescription != null) {
+                    contentDescription.append(state.contentDescription).append(", ");
+                }
+                if (state.roaming) {
+                    contentDescription
+                            .append(mContext.getString(R.string.data_connection_roaming))
+                            .append(", ");
+                }
+                // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+                if (hasValidTypeContentDescription(state.typeContentDescription)) {
+                    contentDescription.append(state.typeContentDescription);
+                }
+                mMobileSignal.setContentDescription(contentDescription);
             }
-            mMobileSignal.setImageLevel(state.mobileSignalIconId);
-            StringBuilder contentDescription = new StringBuilder();
-            if (state.contentDescription != null) {
-                contentDescription.append(state.contentDescription).append(", ");
-            }
-            if (state.roaming) {
-                contentDescription
-                        .append(mContext.getString(R.string.data_connection_roaming))
-                        .append(", ");
-            }
-            // TODO: show mobile data off/no internet text for 5 seconds before carrier text
-            if (hasValidTypeContentDescription(state.typeContentDescription)) {
-                contentDescription.append(state.typeContentDescription);
-            }
-            mMobileSignal.setContentDescription(contentDescription);
         }
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index cb76ee2..6908e5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,7 +42,10 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -75,6 +78,7 @@
     private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
     private int[] mLastSignalLevel = new int[SIM_SLOTS];
     private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
+    private final boolean mProviderModel;
     private final CarrierConfigTracker mCarrierConfigTracker;
 
     private boolean mIsSingleCarrier;
@@ -86,6 +90,9 @@
     private final SignalCallback mSignalCallback = new SignalCallback() {
                 @Override
                 public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
+                    if (mProviderModel) {
+                        return;
+                    }
                     int slotIndex = getSlotIndex(indicators.subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -100,12 +107,91 @@
                             indicators.statusIcon.icon,
                             indicators.statusIcon.contentDescription,
                             indicators.typeContentDescription.toString(),
-                            indicators.roaming
+                            indicators.roaming,
+                            mProviderModel
                     );
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
 
                 @Override
+                public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
+                    if (!mProviderModel) {
+                        return;
+                    }
+                    int slotIndex = getSlotIndex(subId);
+                    if (slotIndex >= SIM_SLOTS) {
+                        Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
+                        return;
+                    }
+                    if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                        Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
+                        return;
+                    }
+
+                    boolean displayCallStrengthIcon =
+                            mCarrierConfigTracker.getCallStrengthConfig(subId);
+
+                    if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
+                        if (statusIcon.visible) {
+                            mInfos[slotIndex] = new CellSignalState(
+                                    true,
+                                    statusIcon.icon,
+                                    statusIcon.contentDescription,
+                                    "",
+                                    false,
+                                    mProviderModel);
+                        } else {
+                            // Whenever the no Calling & SMS state is cleared, switched to the last
+                            // known call strength icon.
+                            if (displayCallStrengthIcon) {
+                                mInfos[slotIndex] = new CellSignalState(
+                                        true,
+                                        mLastSignalLevel[slotIndex],
+                                        mLastSignalLevelDescription[slotIndex],
+                                        "",
+                                        false,
+                                        mProviderModel);
+                            } else {
+                                mInfos[slotIndex] = new CellSignalState(
+                                        true,
+                                        R.drawable.ic_qs_sim_card,
+                                        "",
+                                        "",
+                                        false,
+                                        mProviderModel);
+                            }
+                        }
+                        mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+                    } else {
+                        mLastSignalLevel[slotIndex] = statusIcon.icon;
+                        mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
+                        // Only Shows the call strength icon when the no Calling & SMS icon is not
+                        // shown.
+                        if (mInfos[slotIndex].mobileSignalIconId
+                                != R.drawable.ic_qs_no_calling_sms) {
+                            if (displayCallStrengthIcon) {
+                                mInfos[slotIndex] = new CellSignalState(
+                                        true,
+                                        statusIcon.icon,
+                                        statusIcon.contentDescription,
+                                        "",
+                                        false,
+                                        mProviderModel);
+                            } else {
+                                mInfos[slotIndex] = new CellSignalState(
+                                        true,
+                                        R.drawable.ic_qs_sim_card,
+                                        "",
+                                        "",
+                                        false,
+                                        mProviderModel);
+                            }
+                            mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+                        }
+                    }
+                }
+
+                @Override
                 public void setNoSims(boolean hasNoSims, boolean simDetected) {
                     if (hasNoSims) {
                         for (int i = 0; i < SIM_SLOTS; i++) {
@@ -133,8 +219,14 @@
             @Background Handler bgHandler, @Main Looper mainLooper,
             NetworkController networkController,
             CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
-            CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
+            CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
+            SlotIndexResolver slotIndexResolver) {
 
+        if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+            mProviderModel = true;
+        } else {
+            mProviderModel = false;
+        }
         mActivityStarter = activityStarter;
         mBgHandler = bgHandler;
         mNetworkController = networkController;
@@ -170,7 +262,8 @@
                     R.drawable.ic_qs_no_calling_sms,
                     context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
                     "",
-                    false);
+                    false,
+                    mProviderModel);
             mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
             mLastSignalLevelDescription[i] =
                     context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -258,7 +351,8 @@
             for (int i = 0; i < SIM_SLOTS; i++) {
                 if (mInfos[i].visible
                         && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
-                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
+                    mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
+                            mProviderModel);
                 }
             }
         }
@@ -376,13 +470,15 @@
         private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
         private final Context mContext;
         private final CarrierConfigTracker mCarrierConfigTracker;
+        private final FeatureFlags mFeatureFlags;
         private final SlotIndexResolver mSlotIndexResolver;
 
         @Inject
         public Builder(ActivityStarter activityStarter, @Background Handler handler,
                 @Main Looper looper, NetworkController networkController,
                 CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
-                CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
+                CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
+                SlotIndexResolver slotIndexResolver) {
             mActivityStarter = activityStarter;
             mHandler = handler;
             mLooper = looper;
@@ -390,6 +486,7 @@
             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
             mContext = context;
             mCarrierConfigTracker = carrierConfigTracker;
+            mFeatureFlags = featureFlags;
             mSlotIndexResolver = slotIndexResolver;
         }
 
@@ -401,7 +498,7 @@
         public QSCarrierGroupController build() {
             return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
                     mNetworkController, mCarrierTextControllerBuilder, mContext,
-                    mCarrierConfigTracker, mSlotIndexResolver);
+                    mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index d5efe36..f629a036 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -806,8 +806,7 @@
             return;
         }
 
-        mTelephonyManager.setDataEnabledForReason(
-                TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
+        mTelephonyManager.setDataEnabled(enabled);
         if (disableOtherSubscriptions) {
             final List<SubscriptionInfo> subInfoList =
                     mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
index 8c8f54f..ad073c0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -49,8 +49,8 @@
 ) : UserFileManager, CoreStartable(context) {
     companion object {
         private const val FILES = "files"
-        private const val SHARED_PREFS = "shared_prefs"
-        internal const val ID = "UserFileManager"
+        @VisibleForTesting internal const val SHARED_PREFS = "shared_prefs"
+        @VisibleForTesting internal const val ID = "UserFileManager"
     }
 
    private val broadcastReceiver = object : BroadcastReceiver() {
@@ -85,13 +85,15 @@
                 fileName
             )
         } else {
-            Environment.buildPath(
+            val secondaryFile = Environment.buildPath(
                 context.filesDir,
                 ID,
                 userId.toString(),
                 FILES,
                 fileName
             )
+            ensureParentDirExists(secondaryFile)
+            secondaryFile
         }
     }
 
@@ -114,6 +116,7 @@
             fileName
         )
 
+        ensureParentDirExists(secondaryUserDir)
         return context.getSharedPreferences(secondaryUserDir, mode)
     }
 
@@ -141,4 +144,18 @@
             }
         }
     }
+
+    /**
+     * Checks to see if parent dir of the file exists. If it does not, we create the parent dirs
+     * recursively.
+     */
+    @VisibleForTesting
+    internal fun ensureParentDirExists(file: File) {
+        val parent = file.parentFile
+        if (!parent.exists()) {
+            if (!parent.mkdirs()) {
+                Log.e(ID, "Could not create parent directory for file: ${file.absolutePath}")
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index ec221b7..f2014e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,17 +26,25 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings.Global;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
+import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager.RegistrationCallback;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.AccessibilityContentDescriptions;
 import com.android.settingslib.SignalIcon.MobileIconGroup;
 import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.MobileMappings.Config;
@@ -46,6 +54,8 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.util.CarrierConfigTracker;
 
 import java.io.PrintWriter;
@@ -60,22 +70,33 @@
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final int STATUS_HISTORY_SIZE = 64;
+    private static final int IMS_TYPE_WWAN = 1;
+    private static final int IMS_TYPE_WLAN = 2;
+    private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
     private final TelephonyManager mPhone;
     private final CarrierConfigTracker mCarrierConfigTracker;
+    private final ImsMmTelManager mImsMmTelManager;
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
     private final String mNetworkNameSeparator;
     private final ContentObserver mObserver;
+    private final boolean mProviderModelBehavior;
+    private final Handler mReceiverHandler;
+    private int mImsType = IMS_TYPE_WWAN;
     // Save entire info for logging, we only use the id.
     final SubscriptionInfo mSubscriptionInfo;
     private Map<String, MobileIconGroup> mNetworkToIconLookup;
 
+    private int mLastLevel;
     private MobileIconGroup mDefaultIcons;
     private Config mConfig;
     @VisibleForTesting
     boolean mInflateSignalStrengths = false;
+    private int mLastWwanLevel;
+    private int mLastWlanLevel;
+    private int mLastWlanCrossSimLevel;
     @VisibleForTesting
-    final MobileStatusTracker mMobileStatusTracker;
+    MobileStatusTracker mMobileStatusTracker;
 
     // Save the previous STATUS_HISTORY_SIZE states for logging.
     private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -112,6 +133,52 @@
                 }
             };
 
+    private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
+        @Override
+        public void onRegistered(ImsRegistrationAttributes attributes) {
+            Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
+            int imsTransportType = attributes.getTransportType();
+            int registrationAttributes = attributes.getAttributeFlags();
+            if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+                mImsType = IMS_TYPE_WWAN;
+                IconState statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+            } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+                if (registrationAttributes == 0) {
+                    mImsType = IMS_TYPE_WLAN;
+                    IconState statusIcon = new IconState(
+                            true,
+                            getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+                            getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                } else if (registrationAttributes
+                        == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
+                    mImsType = IMS_TYPE_WLAN_CROSS_SIM;
+                    IconState statusIcon = new IconState(
+                            true,
+                            getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+                            getCallStrengthDescription(
+                                    mLastWlanCrossSimLevel, /* isWifi= */false));
+                    notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+                }
+            }
+        }
+
+        @Override
+        public void onUnregistered(ImsReasonInfo info) {
+            Log.d(mTag, "onDeregistered: " + "info=" + info);
+            mImsType = IMS_TYPE_WWAN;
+            IconState statusIcon = new IconState(
+                    true,
+                    getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                    getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+        }
+    };
+
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
     public MobileSignalController(
@@ -125,7 +192,7 @@
             SubscriptionDefaults defaults,
             Looper receiverLooper,
             CarrierConfigTracker carrierConfigTracker,
-            MobileStatusTrackerFactory mobileStatusTrackerFactory
+            FeatureFlags featureFlags
     ) {
         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -139,6 +206,7 @@
                 R.string.status_bar_network_name_separator).toString();
         mNetworkNameDefault = getTextIfExists(
                 com.android.internal.R.string.lockscreen_carrier_default).toString();
+        mReceiverHandler = new Handler(receiverLooper);
 
         mNetworkToIconLookup = mapIconSets(mConfig);
         mDefaultIcons = getDefaultIcons(mConfig);
@@ -155,7 +223,10 @@
                 updateTelephony();
             }
         };
-        mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
+        mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
+        mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
+                info, mDefaults, mMobileCallback);
+        mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
     }
 
     void setConfiguration(Config config) {
@@ -200,14 +271,41 @@
         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
                 true, mObserver);
+        if (mProviderModelBehavior) {
+            mReceiverHandler.post(mTryRegisterIms);
+        }
     }
 
+    // There is no listener to monitor whether the IMS service is ready, so we have to retry the
+    // IMS registration.
+    private final Runnable mTryRegisterIms = new Runnable() {
+        private static final int MAX_RETRY = 12;
+        private int mRetryCount;
+
+        @Override
+        public void run() {
+            try {
+                mRetryCount++;
+                mImsMmTelManager.registerImsRegistrationCallback(
+                        mReceiverHandler::post, mRegistrationCallback);
+                Log.d(mTag, "registerImsRegistrationCallback succeeded");
+            } catch (RuntimeException | ImsException e) {
+                if (mRetryCount < MAX_RETRY) {
+                    Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
+                    // Wait for 5 seconds to retry
+                    mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
+                }
+            }
+        }
+    };
+
     /**
      * Stop listening for phone state changes.
      */
     public void unregisterListener() {
         mMobileStatusTracker.setListening(false);
         mContext.getContentResolver().unregisterContentObserver(mObserver);
+        mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
     }
 
     private void updateInflateSignalStrength() {
@@ -296,7 +394,7 @@
         CharSequence qsDescription = null;
 
         if (mCurrentState.dataSim) {
-            // only show QS icons if the state is also default
+            // If using provider model behavior, only show QS icons if the state is also default
             if (!mCurrentState.isDefault) {
                 return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
             }
@@ -318,15 +416,32 @@
 
     private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
         final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
-        IconState statusIcon = new IconState(
-                mCurrentState.enabled && !mCurrentState.airplaneMode,
-                getCurrentIconId(), contentDescription);
+        boolean showTriangle = false;
+        int typeIcon = 0;
+        IconState statusIcon = null;
 
-        boolean showDataIconInStatusBar =
-                (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
-        int typeIcon =
-                (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
-        boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
+        if (mProviderModelBehavior) {
+            boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
+                    && (mCurrentState.dataSim && mCurrentState.isDefault);
+            typeIcon =
+                    (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+            showDataIconStatusBar |= mCurrentState.roaming;
+            statusIcon = new IconState(
+                    showDataIconStatusBar && !mCurrentState.airplaneMode,
+                    getCurrentIconId(), contentDescription);
+
+            showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
+        } else {
+            statusIcon = new IconState(
+                    mCurrentState.enabled && !mCurrentState.airplaneMode,
+                    getCurrentIconId(), contentDescription);
+
+            boolean showDataIconInStatusBar =
+                    (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+            typeIcon =
+                    (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+            showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
+        }
 
         return new SbInfo(showTriangle, typeIcon, statusIcon);
     }
@@ -445,7 +560,144 @@
     }
 
     private void updateMobileStatus(MobileStatus mobileStatus) {
+        int lastVoiceState = mCurrentState.getVoiceServiceState();
         mCurrentState.setFromMobileStatus(mobileStatus);
+
+        notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
+        if (mProviderModelBehavior) {
+            maybeNotifyCallStateChanged(lastVoiceState);
+        }
+    }
+
+    /** Call state changed is only applicable when provider model behavior is true */
+    private void maybeNotifyCallStateChanged(int lastVoiceState) {
+        int currentVoiceState = mCurrentState.getVoiceServiceState();
+        if (lastVoiceState == currentVoiceState) {
+            return;
+        }
+        // Only update the no calling Status in the below scenarios
+        // 1. The first valid voice state has been received
+        // 2. The voice state has been changed and either the last or current state is
+        //    ServiceState.STATE_IN_SERVICE
+        if (lastVoiceState == -1
+                || (lastVoiceState == ServiceState.STATE_IN_SERVICE
+                        || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
+            boolean isNoCalling = mCurrentState.isNoCalling();
+            isNoCalling &= !hideNoCalling();
+            IconState statusIcon = new IconState(isNoCalling,
+                    R.drawable.ic_qs_no_calling_sms,
+                    getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+            notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+        }
+    }
+
+    void updateNoCallingState() {
+        int currentVoiceState = mCurrentState.getVoiceServiceState();
+        boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
+        isNoCalling &= !hideNoCalling();
+        IconState statusIcon = new IconState(isNoCalling,
+                R.drawable.ic_qs_no_calling_sms,
+                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    private boolean hideNoCalling() {
+        return mNetworkController.hasDefaultNetwork()
+                && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
+    }
+
+    private int getCallStrengthIcon(int level, boolean isWifi) {
+        return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
+                : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
+    }
+
+    private String getCallStrengthDescription(int level, boolean isWifi) {
+        return isWifi
+                ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
+                        .toString()
+                : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
+                        .toString();
+    }
+
+    void refreshCallIndicator(SignalCallback callback) {
+        boolean isNoCalling = mCurrentState.isNoCalling();
+        isNoCalling &= !hideNoCalling();
+        IconState statusIcon = new IconState(isNoCalling,
+                R.drawable.ic_qs_no_calling_sms,
+                getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+
+        switch (mImsType) {
+            case IMS_TYPE_WWAN:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
+                break;
+            case IMS_TYPE_WLAN:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
+                        getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
+                break;
+            case IMS_TYPE_WLAN_CROSS_SIM:
+                statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
+                        getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
+        }
+        callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyWifiLevelChange(int level) {
+        if (!mProviderModelBehavior) {
+            return;
+        }
+        mLastWlanLevel = level;
+        if (mImsType != IMS_TYPE_WLAN) {
+            return;
+        }
+        IconState statusIcon = new IconState(
+                true,
+                getCallStrengthIcon(level, /* isWifi= */true),
+                getCallStrengthDescription(level, /* isWifi= */true));
+        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyDefaultMobileLevelChange(int level) {
+        if (!mProviderModelBehavior) {
+            return;
+        }
+        mLastWlanCrossSimLevel = level;
+        if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
+            return;
+        }
+        IconState statusIcon = new IconState(
+                true,
+                getCallStrengthIcon(level, /* isWifi= */false),
+                getCallStrengthDescription(level, /* isWifi= */false));
+        notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+    }
+
+    void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
+        if (!mProviderModelBehavior) {
+            return;
+        }
+        int newLevel = getSignalLevel(signalStrength);
+        if (newLevel != mLastLevel) {
+            mLastLevel = newLevel;
+            mLastWwanLevel = newLevel;
+            if (mImsType == IMS_TYPE_WWAN) {
+                IconState statusIcon = new IconState(
+                        true,
+                        getCallStrengthIcon(newLevel, /* isWifi= */false),
+                        getCallStrengthDescription(newLevel, /* isWifi= */false));
+                notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+            }
+            if (mCurrentState.dataSim) {
+                mNetworkController.notifyDefaultMobileLevelChange(newLevel);
+            }
+        }
     }
 
     int getSignalLevel(SignalStrength signalStrength) {
@@ -549,14 +801,19 @@
         mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
     }
 
+    @VisibleForTesting
+    void setImsType(int imsType) {
+        mImsType = imsType;
+    }
+
     @Override
     public void dump(PrintWriter pw) {
         super.dump(pw);
         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
+        pw.println("  mProviderModelBehavior=" + mProviderModelBehavior + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
-        pw.println("  mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
         pw.println("  MobileStatusHistory");
         int size = 0;
         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -586,11 +843,6 @@
             icon = iconState;
             description = desc;
         }
-
-        @Override
-        public String toString() {
-            return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
-        }
     }
 
     /** Box for status bar icon info */
@@ -604,11 +856,5 @@
             ratTypeIcon = typeIcon;
             icon = iconState;
         }
-
-        @Override
-        public String toString() {
-            return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
-                    + " icon=" + icon;
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
deleted file mode 100644
index f0e52f1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2022 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.connectivity
-
-import android.content.Context
-import android.os.Looper
-import android.telephony.SubscriptionInfo
-import android.telephony.TelephonyManager
-import com.android.settingslib.mobile.MobileMappings
-import com.android.settingslib.mobile.MobileStatusTracker
-import com.android.systemui.util.CarrierConfigTracker
-import javax.inject.Inject
-
-/**
- * Factory to make MobileSignalController injectable
- */
-internal class MobileSignalControllerFactory @Inject constructor(
-    val context: Context,
-    val callbackHandler: CallbackHandler,
-    val carrierConfigTracker: CarrierConfigTracker,
-) {
-    fun createMobileSignalController(
-        config: MobileMappings.Config,
-        hasMobileData: Boolean,
-        phone: TelephonyManager,
-        networkController: NetworkControllerImpl,
-        subscriptionInfo: SubscriptionInfo,
-        subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
-        receiverLooper: Looper,
-    ): MobileSignalController {
-        val mobileTrackerFactory = MobileStatusTrackerFactory(
-            phone,
-            receiverLooper,
-            subscriptionInfo,
-            subscriptionDefaults)
-
-        return MobileSignalController(
-            context,
-            config,
-            hasMobileData,
-            phone,
-            callbackHandler,
-            networkController,
-            subscriptionInfo,
-            subscriptionDefaults,
-            receiverLooper,
-            carrierConfigTracker,
-            mobileTrackerFactory,
-        )
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
deleted file mode 100644
index a4c1a198..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2022 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.connectivity
-
-import android.os.Looper
-import android.telephony.SubscriptionInfo
-import android.telephony.TelephonyManager
-import com.android.settingslib.mobile.MobileStatusTracker
-
-/**
- * Factory for [MobileStatusTracker], which lives in SettingsLib
- */
-class MobileStatusTrackerFactory (
-    val phone: TelephonyManager,
-    val receiverLooper: Looper,
-    val info: SubscriptionInfo,
-    val defaults: MobileStatusTracker.SubscriptionDefaults,
-) {
-    fun createTracker(
-        callback: MobileStatusTracker.Callback
-    ): MobileStatusTracker {
-        return MobileStatusTracker(
-            phone,
-            receiverLooper,
-            info,
-            defaults,
-            callback)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index ea7ec4f..268531d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,6 +71,8 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogLevel;
 import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -131,11 +133,12 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DemoModeController mDemoModeController;
     private final Object mLock = new Object();
+    private final boolean mProviderModelBehavior;
     private Config mConfig;
     private final CarrierConfigTracker mCarrierConfigTracker;
+    private final FeatureFlags mFeatureFlags;
     private final DumpManager mDumpManager;
     private final LogBuffer mLogBuffer;
-    private final MobileSignalControllerFactory mMobileFactory;
 
     private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -231,9 +234,9 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
-            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
             InternetDialogFactory internetDialogFactory,
+            FeatureFlags featureFlags,
             DumpManager dumpManager,
             @StatusBarNetworkControllerLog LogBuffer logBuffer) {
         this(context, connectivityManager,
@@ -253,8 +256,8 @@
                 demoModeController,
                 carrierConfigTracker,
                 trackerFactory,
-                mobileFactory,
                 handler,
+                featureFlags,
                 dumpManager,
                 logBuffer);
         mReceiverHandler.post(mRegisterListeners);
@@ -279,8 +282,8 @@
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
             WifiStatusTrackerFactory trackerFactory,
-            MobileSignalControllerFactory mobileFactory,
             @Main Handler handler,
+            FeatureFlags featureFlags,
             DumpManager dumpManager,
             LogBuffer logBuffer
     ) {
@@ -294,7 +297,6 @@
         mCallbackHandler = callbackHandler;
         mDataSaverController = new DataSaverControllerImpl(context);
         mBroadcastDispatcher = broadcastDispatcher;
-        mMobileFactory = mobileFactory;
 
         mSubscriptionManager = subManager;
         mSubDefaults = defaultsHandler;
@@ -302,6 +304,7 @@
         mHasMobileDataFeature = telephonyManager.isDataCapable();
         mDemoModeController = demoModeController;
         mCarrierConfigTracker = carrierConfigTracker;
+        mFeatureFlags = featureFlags;
         mDumpManager = dumpManager;
         mLogBuffer = logBuffer;
 
@@ -453,6 +456,7 @@
         };
 
         mDemoModeController.addCallback(this);
+        mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
 
         mDumpManager.registerDumpable(TAG, this);
     }
@@ -493,16 +497,16 @@
 
         // broadcasts
         IntentFilter filter = new IntentFilter();
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(Intent.ACTION_SERVICE_STATE);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
         filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+        filter.addAction(Intent.ACTION_SERVICE_STATE);
         filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
         mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
         mListening = true;
 
@@ -655,6 +659,20 @@
         return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
     }
 
+    void notifyWifiLevelChange(int level) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+            mobileSignalController.notifyWifiLevelChange(level);
+        }
+    }
+
+    void notifyDefaultMobileLevelChange(int level) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+            mobileSignalController.notifyDefaultMobileLevelChange(level);
+        }
+    }
+
     private void notifyControllersMobileDataChanged() {
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -727,6 +745,9 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners(cb);
+            if (mProviderModelBehavior) {
+                mobileSignalController.refreshCallIndicator(cb);
+            }
         }
         mCallbackHandler.setListening(cb, true);
     }
@@ -841,6 +862,9 @@
         for (int i = 0; i < mMobileSignalControllers.size(); i++) {
             MobileSignalController controller = mMobileSignalControllers.valueAt(i);
             controller.setConfiguration(mConfig);
+            if (mProviderModelBehavior) {
+                controller.refreshCallIndicator(mCallbackHandler);
+            }
         }
         refreshLocale();
     }
@@ -957,15 +981,11 @@
                 mMobileSignalControllers.put(subId, cachedControllers.get(subId));
                 cachedControllers.remove(subId);
             } else {
-                MobileSignalController controller = mMobileFactory.createMobileSignalController(
-                        mConfig,
-                        mHasMobileDataFeature,
-                        mPhone.createForSubscriptionId(subId),
-                        this,
-                        subscriptions.get(i),
-                        mSubDefaults,
-                        mReceiverHandler.getLooper()
-                );
+                MobileSignalController controller = new MobileSignalController(mContext, mConfig,
+                        mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
+                        mCallbackHandler, this, subscriptions.get(i),
+                        mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
+                        mFeatureFlags);
                 controller.setUserSetupComplete(mUserSetup);
                 mMobileSignalControllers.put(subId, controller);
                 if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1119,11 +1139,24 @@
                 || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
 
         pushConnectivityToSignals();
-        mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
-                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
-                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
-        mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
-                mNoNetworksAvailable);
+        if (mProviderModelBehavior) {
+            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+                    mNoNetworksAvailable);
+            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+                mobileSignalController.updateNoCallingState();
+            }
+            notifyAllListeners();
+        } else {
+            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                    && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+            mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+                    mNoNetworksAvailable);
+        }
     }
 
     /**
@@ -1313,7 +1346,7 @@
                 mMobileSignalControllers.clear();
                 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
                 for (int i = start /* get out of normal index range */; i < start + num; i++) {
-                    subs.add(addDemoModeSignalController(i, i));
+                    subs.add(addSignalController(i, i));
                 }
                 mCallbackHandler.setSubs(subs);
                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1339,7 +1372,7 @@
             List<SubscriptionInfo> subs = new ArrayList<>();
             while (mMobileSignalControllers.size() <= slot) {
                 int nextSlot = mMobileSignalControllers.size();
-                subs.add(addDemoModeSignalController(nextSlot, nextSlot));
+                subs.add(addSignalController(nextSlot, nextSlot));
             }
             if (!subs.isEmpty()) {
                 mCallbackHandler.setSubs(subs);
@@ -1429,20 +1462,14 @@
         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
     }
 
-    private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
+    private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
         SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
                 null, null, null, "", false, null, null);
-
-        MobileSignalController controller = mMobileFactory.createMobileSignalController(
-                mConfig,
-                mHasMobileDataFeature,
-                mPhone.createForSubscriptionId(info.getSubscriptionId()),
-                this,
-                info,
-                mSubDefaults,
-                mReceiverHandler.getLooper()
-        );
-
+        MobileSignalController controller = new MobileSignalController(mContext,
+                mConfig, mHasMobileDataFeature,
+                mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
+                info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
+                mFeatureFlags);
         mMobileSignalControllers.put(id, controller);
         controller.getState().userSetup = true;
         return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 12f2c22..87cdb17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,6 +222,7 @@
         mCurrentState.connected = mWifiTracker.connected;
         mCurrentState.ssid = mWifiTracker.ssid;
         mCurrentState.rssi = mWifiTracker.rssi;
+        boolean levelChanged = mCurrentState.level != mWifiTracker.level;
         mCurrentState.level = mWifiTracker.level;
         mCurrentState.statusLabel = mWifiTracker.statusLabel;
         mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -229,6 +230,10 @@
         mCurrentState.iconGroup =
                 mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
                         : mUnmergedWifiIconGroup;
+
+        if (levelChanged) {
+            mNetworkController.notifyWifiLevelChange(mCurrentState.level);
+        }
     }
 
     boolean isCarrierMergedWifi(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 492734e..ee242a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,6 +26,8 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -64,6 +66,7 @@
     private final Handler mHandler = Handler.getMain();
     private final CarrierConfigTracker mCarrierConfigTracker;
     private final TunerService mTunerService;
+    private final FeatureFlags mFeatureFlags;
 
     private boolean mHideAirplane;
     private boolean mHideMobile;
@@ -87,7 +90,8 @@
             CarrierConfigTracker carrierConfigTracker,
             NetworkController networkController,
             SecurityController securityController,
-            TunerService tunerService
+            TunerService tunerService,
+            FeatureFlags featureFlags
     ) {
         mContext = context;
 
@@ -96,6 +100,7 @@
         mNetworkController = networkController;
         mSecurityController = securityController;
         mTunerService = tunerService;
+        mFeatureFlags = featureFlags;
 
         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -373,6 +378,40 @@
     }
 
     @Override
+    public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
+            boolean noNetworksAvailable) {
+        if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+            return;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "setConnectivityStatus: "
+                    + "noDefaultNetwork = " + noDefaultNetwork + ","
+                    + "noValidatedNetwork = " + noValidatedNetwork + ","
+                    + "noNetworksAvailable = " + noNetworksAvailable);
+        }
+        WifiIconState newState = mWifiIconState.copy();
+        newState.noDefaultNetwork = noDefaultNetwork;
+        newState.noValidatedNetwork = noValidatedNetwork;
+        newState.noNetworksAvailable = noNetworksAvailable;
+        newState.slot = mSlotWifi;
+        newState.airplaneSpacerVisible = mIsAirplaneMode;
+        if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
+            newState.visible = true;
+            newState.resId = R.drawable.ic_qs_no_internet_unavailable;
+        } else if (noDefaultNetwork && !noNetworksAvailable
+                && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
+            newState.visible = true;
+            newState.resId = R.drawable.ic_qs_no_internet_available;
+        } else {
+            newState.visible = false;
+            newState.resId = 0;
+        }
+        updateWifiIconWithState(newState);
+        mWifiIconState = newState;
+    }
+
+
+    @Override
     public void setEthernetIndicators(IconState state) {
         boolean visible = state.visible && !mHideEthernet;
         int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 2e66648..dfcdaef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -62,7 +62,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.users.UserCreatingDialog;
-import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.GuestResetOrExitSessionReceiver;
 import com.android.systemui.GuestResumeSessionReceiver;
@@ -167,6 +166,7 @@
     private final AtomicBoolean mGuestIsResetting;
     private final AtomicBoolean mGuestCreationScheduled;
     private FalsingManager mFalsingManager;
+    @Nullable
     private View mView;
     private String mCreateSupervisedUserPackage;
     private GlobalSettings mGlobalSettings;
@@ -572,9 +572,11 @@
 
     protected void switchToUserId(int id) {
         try {
-            mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
-                    .withView(InteractionJankMonitor.CUJ_USER_SWITCH, mView)
-                    .setTimeout(MULTI_USER_JOURNEY_TIMEOUT));
+            if (mView != null) {
+                mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
+                        .withView(InteractionJankMonitor.CUJ_USER_SWITCH, mView)
+                        .setTimeout(MULTI_USER_JOURNEY_TIMEOUT));
+            }
             mLatencyTracker.onActionStart(LatencyTracker.ACTION_USER_SWITCH);
             pauseRefreshUsers();
             mActivityManager.switchUser(id);
@@ -936,15 +938,17 @@
         guestCreationProgressDialog.show();
 
         // userManager.createGuest will block the thread so post is needed for the dialog to show
-        ThreadUtils.postOnMainThread(() -> {
+        mBgExecutor.execute(() -> {
             final int guestId = createGuest();
-            guestCreationProgressDialog.dismiss();
-            if (guestId == UserHandle.USER_NULL) {
-                Toast.makeText(mContext,
-                        com.android.settingslib.R.string.add_guest_failed,
-                        Toast.LENGTH_SHORT).show();
-            }
-            callback.accept(guestId);
+            mUiExecutor.execute(() -> {
+                guestCreationProgressDialog.dismiss();
+                if (guestId == UserHandle.USER_NULL) {
+                    Toast.makeText(mContext,
+                            com.android.settingslib.R.string.add_guest_failed,
+                            Toast.LENGTH_SHORT).show();
+                }
+                callback.accept(guestId);
+            });
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
deleted file mode 100644
index 0686071c..0000000
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.user;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.UserManager;
-
-import com.android.internal.util.UserIcons;
-import com.android.settingslib.users.UserCreatingDialog;
-import com.android.settingslib.utils.ThreadUtils;
-
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * A class to do the user creation process. It shows a progress dialog, and manages the user
- * creation
- */
-public class UserCreator {
-
-    private final Context mContext;
-    private final UserManager mUserManager;
-
-    @Inject
-    public UserCreator(Context context, UserManager userManager) {
-        mContext = context;
-        mUserManager = userManager;
-    }
-
-    /**
-     * Shows a progress dialog then starts the user creation process on the main thread.
-     *
-     * @param successCallback is called when the user creation is successful.
-     * @param errorCallback   is called when userManager.createUser returns null.
-     *                        (Exceptions are not handled by this class)
-     */
-    public void createUser(String userName, Drawable userIcon, Consumer<UserInfo> successCallback,
-            Runnable errorCallback) {
-
-        Dialog userCreationProgressDialog = new UserCreatingDialog(mContext);
-        userCreationProgressDialog.show();
-
-        // userManager.createUser will block the thread so post is needed for the dialog to show
-        ThreadUtils.postOnMainThread(() -> {
-            UserInfo user =
-                    mUserManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0);
-            if (user == null) {
-                // Couldn't create user for some reason
-                userCreationProgressDialog.dismiss();
-                errorCallback.run();
-                return;
-            }
-
-            Drawable newUserIcon = userIcon;
-            Resources res = mContext.getResources();
-            if (newUserIcon == null) {
-                newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
-            }
-            mUserManager.setUserIcon(
-                    user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));
-
-            userCreationProgressDialog.dismiss();
-            successCallback.accept(user);
-        });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
new file mode 100644
index 0000000..dcbbe74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.user
+
+import android.app.Dialog
+import android.content.Context
+import android.content.pm.UserInfo
+import android.graphics.drawable.Drawable
+import android.os.UserManager
+import com.android.internal.util.UserIcons
+import com.android.settingslib.users.UserCreatingDialog
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * A class to do the user creation process. It shows a progress dialog, and manages the user
+ * creation
+ */
+class UserCreator @Inject constructor(
+    private val context: Context,
+    private val userManager: UserManager,
+    @Main private val mainExecutor: Executor,
+    @Background private val bgExecutor: Executor
+) {
+    /**
+     * Shows a progress dialog then starts the user creation process on the main thread.
+     *
+     * @param successCallback is called when the user creation is successful.
+     * @param errorCallback is called when userManager.createUser returns null.
+     * (Exceptions are not handled by this class)
+     */
+    fun createUser(
+        userName: String?,
+        userIcon: Drawable?,
+        successCallback: Consumer<UserInfo?>,
+       errorCallback: Runnable
+    ) {
+        val userCreationProgressDialog: Dialog = UserCreatingDialog(context)
+        userCreationProgressDialog.show()
+
+        // userManager.createUser will block the thread so post is needed for the dialog to show
+        bgExecutor.execute {
+            val user = userManager.createUser(userName, UserManager.USER_TYPE_FULL_SECONDARY, 0)
+            mainExecutor.execute main@{
+                if (user == null) {
+                    // Couldn't create user for some reason
+                    userCreationProgressDialog.dismiss()
+                    errorCallback.run()
+                    return@main
+                }
+                bgExecutor.execute {
+                    var newUserIcon = userIcon
+                    val res = context.resources
+                    if (newUserIcon == null) {
+                        newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false)
+                    }
+                    userManager.setUserIcon(
+                        user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon))
+                }
+                userCreationProgressDialog.dismiss()
+                successCallback.accept(user)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index be14cc5..07c8af9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -45,10 +46,10 @@
 import org.mockito.Answers
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -161,6 +162,7 @@
 
     @Test
     fun testRSSISlot_notCombined() {
+        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
         controller.init()
 
         val captor = argumentCaptor<List<String>>()
@@ -172,6 +174,20 @@
     }
 
     @Test
+    fun testRSSISlot_combined() {
+        `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
+        controller.init()
+
+        val captor = argumentCaptor<List<String>>()
+        verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
+
+        assertThat(captor.value).containsExactly(
+            mContext.getString(com.android.internal.R.string.status_bar_no_calling),
+            mContext.getString(com.android.internal.R.string.status_bar_call_strength)
+        )
+    }
+
+    @Test
     fun testSingleCarrierCallback() {
         controller.init()
         reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 1963e30..bd794d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -37,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -79,6 +80,7 @@
     @Mock
     private QSCarrier mQSCarrier3;
     private TestableLooper mTestableLooper;
+    @Mock private FeatureFlags mFeatureFlags;
     @Mock
     private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
 
@@ -118,7 +120,7 @@
         mQSCarrierGroupController = new QSCarrierGroupController.Builder(
                 mActivityStarter, handler, TestableLooper.get(this).getLooper(),
                 mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
-                mSlotIndexResolver)
+                mFeatureFlags, mSlotIndexResolver)
                 .setQSCarrierGroup(mQSCarrierGroup)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 99a17a6..5212255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,11 +22,13 @@
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.FeatureFlagUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.graph.SignalDrawable;
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -57,14 +59,14 @@
 
     @Test
     public void testUpdateState_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         assertTrue(mQSCarrier.updateState(c, false));
     }
 
     @Test
     public void testUpdateState_same() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         assertTrue(mQSCarrier.updateState(c, false));
         assertFalse(mQSCarrier.updateState(c, false));
@@ -72,7 +74,7 @@
 
     @Test
     public void testUpdateState_changed() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         assertTrue(mQSCarrier.updateState(c, false));
 
@@ -83,14 +85,14 @@
 
     @Test
     public void testUpdateState_singleCarrier_first() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         assertTrue(mQSCarrier.updateState(c, true));
     }
 
     @Test
     public void testUpdateState_singleCarrier_noShowIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         mQSCarrier.updateState(c, true);
 
@@ -99,7 +101,7 @@
 
     @Test
     public void testUpdateState_multiCarrier_showIcon() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         mQSCarrier.updateState(c, false);
 
@@ -108,7 +110,7 @@
 
     @Test
     public void testUpdateState_changeSingleMultiSingle() {
-        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
+        CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
 
         mQSCarrier.updateState(c, true);
         assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
index 73226fa..6d9b01e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.Executor
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,6 +63,14 @@
             broadcastDispatcher, backgroundExecutor)
     }
 
+    @After
+    fun end() {
+        val dir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID)
+        dir.deleteRecursively()
+    }
+
     @Test
     fun testGetFile() {
         assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
@@ -72,8 +81,19 @@
 
     @Test
     fun testGetSharedPreferences() {
+        val secondarySharedPref = userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)
+        val secondaryUserDir = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            UserFileManagerImpl.SHARED_PREFS,
+            TEST_FILE_NAME
+        )
+
+        assertThat(secondarySharedPref).isNotNull()
+        assertThat(secondaryUserDir.exists())
         assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
-            .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11))
+            .isNotEqualTo(secondarySharedPref)
     }
 
     @Test
@@ -115,6 +135,19 @@
         verify(userManager).aliveUsers
         assertThat(secondaryUserDir.exists()).isFalse()
         assertThat(file.exists()).isFalse()
-        dir.deleteRecursively()
+    }
+
+    @Test
+    fun testEnsureParentDirExists() {
+        val file = Environment.buildPath(
+            context.filesDir,
+            UserFileManagerImpl.ID,
+            "11",
+            "files",
+            TEST_FILE_NAME
+        )
+        assertThat(file.parentFile.exists()).isFalse()
+        userFileManager.ensureParentDirExists(file)
+        assertThat(file.parentFile.exists()).isTrue()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index f8a0d2f..0d1879c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,6 +70,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -125,8 +127,8 @@
     protected CarrierConfigTracker mCarrierConfigTracker;
     protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     protected Handler mMainHandler;
+    protected FeatureFlags mFeatureFlags;
     protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
-    protected MobileSignalControllerFactory mMobileFactory;
 
     protected int mSubId;
 
@@ -156,6 +158,9 @@
 
     @Before
     public void setUp() throws Exception {
+        mFeatureFlags = mock(FeatureFlags.class);
+        when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
+
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
         TestableResources res = mContext.getOrCreateTestableResources();
@@ -219,11 +224,6 @@
 
         mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
                 mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
-        mMobileFactory = new MobileSignalControllerFactory(
-                mContext,
-                mCallbackHandler,
-                mCarrierConfigTracker
-        );
 
         mNetworkController = new NetworkControllerImpl(mContext,
                 mMockCm,
@@ -243,8 +243,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -438,6 +438,10 @@
         updateSignalStrength();
     }
 
+    public void setImsType(int imsType) {
+        mMobileSignalController.setImsType(imsType);
+    }
+
     public void setIsGsm(boolean gsm) {
         when(mSignalStrength.isGsm()).thenReturn(gsm);
         updateSignalStrength();
@@ -633,4 +637,5 @@
     protected void assertDataNetworkNameEquals(String expected) {
         assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
     }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index ed8a3e1..e3dd6f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 new Handler(TestableLooper.get(this).getLooper()),
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index a76676e..698899a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class)
         );
@@ -121,8 +121,8 @@
                 mDemoModeController,
                 mCarrierConfigTracker,
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
@@ -192,8 +192,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         mNetworkController.registerListeners();
@@ -277,8 +277,8 @@
                 mDemoModeController,
                 mock(CarrierConfigTracker.class),
                 mWifiStatusTrackerFactory,
-                mMobileFactory,
                 mMainHandler,
+                mFeatureFlags,
                 mock(DumpManager.class),
                 mock(LogBuffer.class));
         setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 68170ea..3f71491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -30,6 +30,7 @@
 import android.net.vcn.VcnTransportInfo;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
+import android.telephony.CellSignalStrength;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -284,6 +285,44 @@
         verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
     }
 
+    @Test
+    public void testCallStrengh() {
+        if (true) return;
+        String testSsid = "Test SSID";
+        setWifiEnabled(true);
+        setWifiState(true, testSsid);
+        // Set the ImsType to be IMS_TYPE_WLAN
+        setImsType(2);
+        setWifiLevel(1);
+        for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
+            setWifiLevel(testLevel);
+            verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
+        }
+        // Set the ImsType to be IMS_TYPE_WWAN
+        setImsType(1);
+        setupDefaultSignal();
+        for (int testStrength = 0;
+                testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
+            setLevel(testStrength);
+            verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
+        }
+    }
+
+    @Test
+    public void testNonPrimaryWiFi() {
+        if (true) return;
+        String testSsid = "Test SSID";
+        setWifiEnabled(true);
+        setWifiState(true, testSsid);
+        // Set the ImsType to be IMS_TYPE_WLAN
+        setImsType(2);
+        setWifiLevel(1);
+        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
+        when(mWifiInfo.isPrimary()).thenReturn(false);
+        setWifiLevel(3);
+        verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
+    }
+
     protected void setWifiActivity(int activity) {
         // TODO: Not this, because this variable probably isn't sticking around.
         mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 09d7c03..359a780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -77,6 +77,7 @@
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
@@ -269,6 +270,8 @@
         `when`(userManager.createGuest(any())).thenReturn(guestInfo)
 
         userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, null)
+        bgExecutor.runAllReady()
+        uiExecutor.runAllReady()
         testableLooper.processAllMessages()
         verify(interactionJankMonitor).begin(any())
         verify(latencyTracker).onActionStart(LatencyTracker.ACTION_USER_SWITCH)
@@ -294,6 +297,8 @@
         `when`(userManager.createGuest(any())).thenReturn(guestInfo)
 
         userSwitcherController.onUserListItemClicked(emptyGuestUserRecord, dialogShower)
+        bgExecutor.runAllReady()
+        uiExecutor.runAllReady()
         testableLooper.processAllMessages()
         verify(dialogShower).dismiss()
     }
@@ -584,4 +589,24 @@
         broadcastReceiverCaptor.value.onReceive(context, intent)
         verify(cb).onUserSwitched()
     }
+
+    @Test
+    fun onUserItemClicked_guest_runsOnBgThread() {
+        val dialogShower = mock(UserSwitchDialogController.DialogShower::class.java)
+        val guestUserRecord = UserSwitcherController.UserRecord(
+            null,
+            picture,
+            true /* guest */,
+            false /* current */,
+            false /* isAddUser */,
+            false /* isRestricted */,
+            true /* isSwitchToEnabled */,
+            false /* isAddSupervisedUser */)
+
+        userSwitcherController.onUserListItemClicked(guestUserRecord, dialogShower)
+        assertTrue(bgExecutor.numPending() > 0)
+        verify(userManager, never()).createGuest(context)
+        bgExecutor.runAllReady()
+        verify(userManager).createGuest(context)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
new file mode 100644
index 0000000..a85ae7df
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
@@ -0,0 +1,73 @@
+package com.android.systemui.user
+
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.os.UserManager
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class UserCreatorTest : SysuiTestCase() {
+    companion object {
+        const val USER_NAME = "abc"
+    }
+
+    @Mock
+    private lateinit var userCreator: UserCreator
+    @Mock
+    private lateinit var userManager: UserManager
+    private lateinit var mainExecutor: FakeExecutor
+    private lateinit var bgExecutor: FakeExecutor
+    private lateinit var user: UserInfo
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mainExecutor = FakeExecutor(FakeSystemClock())
+        bgExecutor = FakeExecutor(FakeSystemClock())
+        userCreator = UserCreator(context, userManager, mainExecutor, bgExecutor)
+        user = Mockito.mock(UserInfo::class.java)
+        Mockito.`when`(userManager.createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0))
+            .thenReturn(user)
+    }
+
+    @Test
+    fun testCreateUser_threadingOrder() {
+        val successCallback = Mockito.mock(Consumer::class.java)
+        val errorCallback = Mockito.mock(Runnable::class.java)
+
+        userCreator.createUser(
+            USER_NAME,
+            null,
+            successCallback as Consumer<UserInfo?>,
+            errorCallback)
+
+        verify(userManager, never()).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)
+        bgExecutor.runAllReady()
+        verify(successCallback, never()).accept(user)
+        mainExecutor.runAllReady()
+        verify(userManager, never()).setUserIcon(anyInt(), any(Bitmap::class.java))
+        bgExecutor.runAllReady()
+
+        verify(userManager).createUser(USER_NAME, UserManager.USER_TYPE_FULL_SECONDARY, 0)
+        verify(userManager).setUserIcon(anyInt(), any(Bitmap::class.java))
+        verify(successCallback).accept(user)
+    }
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 73d9cc7..d29e25c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -862,10 +862,10 @@
         return list;
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.SHUTDOWN)
     @Override
     public void shutdown() {
         // TODO: remove from aidl if nobody calls externally
-        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
 
         Slog.i(TAG, "Shutting down");
     }
@@ -1203,9 +1203,9 @@
         setUidOnMeteredNetworkList(uid, true, enable);
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.NETWORK_SETTINGS)
     @Override
     public boolean setDataSaverModeEnabled(boolean enable) {
-        mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
 
         if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
         synchronized (mQuotaLock) {
@@ -1741,9 +1741,9 @@
         return NetdUtils.removeRoutesFromLocalNetwork(mNetdService, routes);
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
     @Override
     public boolean isNetworkRestricted(int uid) {
-        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         return isNetworkRestrictedInternal(uid);
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c5b5fb9..002aa77 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4413,7 +4413,7 @@
             mAm.notifyPackageUse(r.serviceInfo.packageName,
                                  PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
             thread.scheduleCreateService(r, r.serviceInfo,
-                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
+                    null /* compatInfo (unused but need to keep method signature) */,
                     app.mState.getReportedProcState());
             r.postNotification();
             created = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9f0aeec..5729a06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4980,7 +4980,6 @@
                              PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
             try {
                 thread.scheduleCreateBackupAgent(backupTarget.appInfo,
-                        compatibilityInfoForPackage(backupTarget.appInfo),
                         backupTarget.backupMode, backupTarget.userId, backupTarget.operationType);
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
@@ -12900,8 +12899,7 @@
             if (thread != null) {
                 if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc);
                 try {
-                    thread.scheduleCreateBackupAgent(app,
-                            compatibilityInfoForPackage(app), backupMode, targetUserId,
+                    thread.scheduleCreateBackupAgent(app, backupMode, targetUserId,
                             operationType);
                 } catch (RemoteException e) {
                     // Will time out on the backup manager side
@@ -13027,8 +13025,7 @@
                 final IApplicationThread thread = proc.getThread();
                 if (thread != null) {
                     try {
-                        thread.scheduleDestroyBackupAgent(appInfo,
-                                compatibilityInfoForPackage(appInfo), userId);
+                        thread.scheduleDestroyBackupAgent(appInfo, userId);
                     } catch (Exception e) {
                         Slog.e(TAG, "Exception when unbinding backup agent:");
                         e.printStackTrace();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 0cd1bfa..6d520c3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -361,7 +361,7 @@
             mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                       PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
             thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
-                    mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
+                    null /* compatInfo (unused but need to keep method signature) */,
                     r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                     app.mState.getReportedProcState());
             if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 7371d07..33e4070 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -564,10 +564,9 @@
         return res;
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
@@ -619,11 +618,10 @@
      * @return List of proto binary of individual commit files or one that is merged from them;
      *         the merged, final ProcessStats object.
      */
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public long getCommittedStatsMerged(long highWaterMarkMs, int section, boolean doAggregate,
             List<ParcelFileDescriptor> committedStats, ProcessStats mergedStats) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
 
         long newHighWaterMark = highWaterMarkMs;
         mFileLock.lock();
@@ -708,10 +706,9 @@
         return fds[0];
     }
 
+    @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     @Override
     public ParcelFileDescriptor getStatsOverTime(long minTime) {
-        mAm.mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         long curTime;
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 18d5194..e409256 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3904,6 +3904,36 @@
         }
     }
 
+    private void setLeAudioVolumeOnModeUpdate(int mode) {
+        switch (mode) {
+            case AudioSystem.MODE_IN_COMMUNICATION:
+            case AudioSystem.MODE_IN_CALL:
+            case AudioSystem.MODE_NORMAL:
+                break;
+            case AudioSystem.MODE_RINGTONE:
+                // not changing anything for ringtone
+                return;
+            case AudioSystem.MODE_CURRENT:
+            case AudioSystem.MODE_INVALID:
+            default:
+                // don't know what to do in this case, better bail
+                return;
+        }
+
+        int streamType = getBluetoothContextualVolumeStream(mode);
+
+        // Currently, DEVICE_OUT_BLE_HEADSET is the only output type for LE_AUDIO profile.
+        // (See AudioDeviceBroker#createBtDeviceInfo())
+        int index = mStreamStates[streamType].getIndex(AudioSystem.DEVICE_OUT_BLE_HEADSET);
+        int maxIndex = mStreamStates[streamType].getMaxIndex();
+
+        if (DEBUG_VOL) {
+            Log.d(TAG, "setLeAudioVolumeOnModeUpdate postSetLeAudioVolumeIndex index="
+                    + index + " maxIndex=" + maxIndex + " streamType=" + streamType);
+        }
+        mDeviceBroker.postSetLeAudioVolumeIndex(index, maxIndex, streamType);
+    }
+
     private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
             String caller, String attributionTag, int uid,
             boolean hasModifyAudioSettings) {
@@ -5276,6 +5306,10 @@
                 // change of mode may require volume to be re-applied on some devices
                 updateAbsVolumeMultiModeDevices(previousMode, mode);
 
+                // Forcefully set LE audio volume as a workaround, since the value of 'device'
+                // is not DEVICE_OUT_BLE_* even when BLE is connected.
+                setLeAudioVolumeOnModeUpdate(mode);
+
                 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
                 // connections not started by the application changing the mode when pid changes
                 mDeviceBroker.postSetModeOwnerPid(pid, mode);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index e37a8e0..5e20dfb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -412,7 +412,7 @@
                 enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked(
                         context.getResources(), imi);
             }
-            return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes);
+            return InputMethodSubtype.sort(imi, enabledSubtypes);
         }
 
         List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index 371ef76..b06af8e 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -17,6 +17,7 @@
 package com.android.server.locksettings;
 
 import android.security.AndroidKeyStoreMaintenance;
+import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
@@ -210,10 +211,26 @@
                         .setBoundToSpecificSecureUserId(sid)
                         .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_VALIDITY);
             }
+            final KeyProtection protNonRollbackResistant = builder.build();
+            builder.setRollbackResistant(true);
+            final KeyProtection protRollbackResistant = builder.build();
+            final KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(keyStoreKey);
+            try {
+                keyStore.setEntry(keyAlias, entry, protRollbackResistant);
+                Slog.i(TAG, "Using rollback-resistant key");
+            } catch (KeyStoreException e) {
+                if (!(e.getCause() instanceof android.security.KeyStoreException)) {
+                    throw e;
+                }
+                int errorCode = ((android.security.KeyStoreException) e.getCause()).getErrorCode();
+                if (errorCode != KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE) {
+                    throw e;
+                }
+                Slog.w(TAG, "Rollback-resistant keys unavailable.  Falling back to "
+                        + "non-rollback-resistant key");
+                keyStore.setEntry(keyAlias, entry, protNonRollbackResistant);
+            }
 
-            keyStore.setEntry(keyAlias,
-                    new KeyStore.SecretKeyEntry(keyStoreKey),
-                    builder.build());
             byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data);
             return encrypt(keyStoreKey, intermediate);
         } catch (CertificateException | IOException | BadPaddingException
diff --git a/services/core/java/com/android/server/pm/ApexPackageInfo.java b/services/core/java/com/android/server/pm/ApexPackageInfo.java
index f959a52..73cb0ad 100644
--- a/services/core/java/com/android/server/pm/ApexPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ApexPackageInfo.java
@@ -22,10 +22,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.apex.ApexInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.util.PrintWriterPrinter;
 
 import com.android.internal.annotations.GuardedBy;
@@ -33,8 +32,9 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
 import java.io.File;
@@ -59,16 +59,17 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private List<PackageInfo> mAllPackagesCache;
+    private List<Pair<ApexInfo, AndroidPackage>> mAllPackagesCache;
 
-    /**
-     * Whether an APEX package is active or not.
-     *
-     * @param packageInfo the package to check
-     * @return {@code true} if this package is active, {@code false} otherwise.
-     */
-    private static boolean isActive(PackageInfo packageInfo) {
-        return packageInfo.isActiveApex;
+    @Nullable
+    private final PackageManagerService mPackageManager;
+
+    ApexPackageInfo() {
+        mPackageManager = null;
+    }
+
+    ApexPackageInfo(@NonNull PackageManagerService pms) {
+        mPackageManager = pms;
     }
 
     /**
@@ -105,20 +106,23 @@
      *         is not found.
      */
     @Nullable
-    PackageInfo getPackageInfo(String packageName, @ApexManager.PackageInfoFlags int flags) {
+    Pair<ApexInfo, AndroidPackage> getPackageInfo(String packageName,
+            @ApexManager.PackageInfoFlags int flags) {
         synchronized (mLock) {
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
             boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
             boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
             for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
-                final PackageInfo packageInfo = mAllPackagesCache.get(i);
-                if (!packageInfo.packageName.equals(packageName)) {
+                final Pair<ApexInfo, AndroidPackage> pair = mAllPackagesCache.get(i);
+                var apexInfo = pair.first;
+                var pkg = pair.second;
+                if (!pkg.getPackageName().equals(packageName)) {
                     continue;
                 }
-                if ((matchActive && isActive(packageInfo))
-                        || (matchFactory && isFactory(packageInfo))) {
-                    return packageInfo;
+                if ((matchActive && apexInfo.isActive)
+                        || (matchFactory && apexInfo.isFactory)) {
+                    return pair;
                 }
             }
             return null;
@@ -128,18 +132,18 @@
     /**
      * Retrieves information about all active APEX packages.
      *
-     * @return a List of PackageInfo object, each one containing information about a different
-     *         active package.
+     * @return list containing information about different active packages.
      */
-    List<PackageInfo> getActivePackages() {
+    @NonNull
+    List<Pair<ApexInfo, AndroidPackage>> getActivePackages() {
         synchronized (mLock) {
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
-            final List<PackageInfo> activePackages = new ArrayList<>();
+            final List<Pair<ApexInfo, AndroidPackage>> activePackages = new ArrayList<>();
             for (int i = 0; i < mAllPackagesCache.size(); i++) {
-                final PackageInfo packageInfo = mAllPackagesCache.get(i);
-                if (isActive(packageInfo)) {
-                    activePackages.add(packageInfo);
+                final var pair = mAllPackagesCache.get(i);
+                if (pair.first.isActive) {
+                    activePackages.add(pair);
                 }
             }
             return activePackages;
@@ -147,20 +151,20 @@
     }
 
     /**
-     * Retrieves information about all active pre-installed APEX packages.
+     * Retrieves information about all pre-installed APEX packages.
      *
-     * @return a List of PackageInfo object, each one containing information about a different
-     *         active pre-installed package.
+     * @return list containing information about different pre-installed packages.
      */
-    List<PackageInfo> getFactoryPackages() {
+    @NonNull
+    List<Pair<ApexInfo, AndroidPackage>> getFactoryPackages() {
         synchronized (mLock) {
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
-            final List<PackageInfo> factoryPackages = new ArrayList<>();
+            final List<Pair<ApexInfo, AndroidPackage>> factoryPackages = new ArrayList<>();
             for (int i = 0; i < mAllPackagesCache.size(); i++) {
-                final PackageInfo packageInfo = mAllPackagesCache.get(i);
-                if (isFactory(packageInfo)) {
-                    factoryPackages.add(packageInfo);
+                final var pair = mAllPackagesCache.get(i);
+                if (pair.first.isFactory) {
+                    factoryPackages.add(pair);
                 }
             }
             return factoryPackages;
@@ -170,18 +174,18 @@
     /**
      * Retrieves information about all inactive APEX packages.
      *
-     * @return a List of PackageInfo object, each one containing information about a different
-     *         inactive package.
+     * @return list containing information about different inactive packages.
      */
-    List<PackageInfo> getInactivePackages() {
+    @NonNull
+    List<Pair<ApexInfo, AndroidPackage>> getInactivePackages() {
         synchronized (mLock) {
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
-            final List<PackageInfo> inactivePackages = new ArrayList<>();
+            final List<Pair<ApexInfo, AndroidPackage>> inactivePackages = new ArrayList<>();
             for (int i = 0; i < mAllPackagesCache.size(); i++) {
-                final PackageInfo packageInfo = mAllPackagesCache.get(i);
-                if (!isActive(packageInfo)) {
-                    inactivePackages.add(packageInfo);
+                final var pair = mAllPackagesCache.get(i);
+                if (!pair.first.isActive) {
+                    inactivePackages.add(pair);
                 }
             }
             return inactivePackages;
@@ -199,8 +203,8 @@
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
             for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
-                final PackageInfo packageInfo = mAllPackagesCache.get(i);
-                if (packageInfo.packageName.equals(packageName)) {
+                final var pair = mAllPackagesCache.get(i);
+                if (pair.second.getPackageName().equals(packageName)) {
                     return true;
                 }
             }
@@ -222,21 +226,18 @@
     }
 
     void notifyPackageInstalled(ApexInfo apexInfo, AndroidPackage pkg) {
-        final int flags = PackageManager.GET_META_DATA
-                | PackageManager.GET_SIGNING_CERTIFICATES
-                | PackageManager.GET_SIGNATURES;
-        final PackageInfo newApexPkg = PackageInfoWithoutStateUtils.generate(
-                pkg, apexInfo, flags);
-        final String packageName = newApexPkg.packageName;
+        final String packageName = pkg.getPackageName();
         synchronized (mLock) {
             for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
-                PackageInfo oldApexPkg = mAllPackagesCache.get(i);
-                if (oldApexPkg.isActiveApex && oldApexPkg.packageName.equals(packageName)) {
-                    if (isFactory(oldApexPkg)) {
-                        oldApexPkg.isActiveApex = false;
-                        mAllPackagesCache.add(newApexPkg);
+                var pair = mAllPackagesCache.get(i);
+                var oldApexInfo = pair.first;
+                var oldApexPkg = pair.second;
+                if (oldApexInfo.isActive && oldApexPkg.getPackageName().equals(packageName)) {
+                    if (oldApexInfo.isFactory) {
+                        oldApexInfo.isActive = false;
+                        mAllPackagesCache.add(Pair.create(apexInfo, pkg));
                     } else {
-                        mAllPackagesCache.set(i, newApexPkg);
+                        mAllPackagesCache.set(i, Pair.create(apexInfo, pkg));
                     }
                     break;
                 }
@@ -245,16 +246,6 @@
     }
 
     /**
-     * Whether the APEX package is pre-installed or not.
-     *
-     * @param packageInfo the package to check
-     * @return {@code true} if this package is pre-installed, {@code false} otherwise.
-     */
-    private static boolean isFactory(@NonNull PackageInfo packageInfo) {
-        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0;
-    }
-
-    /**
      * Dumps various state information to the provided {@link PrintWriter} object.
      *
      * @param pw the {@link PrintWriter} object to send information to.
@@ -288,33 +279,25 @@
         HashSet<String> factoryPackagesSet = new HashSet<>();
         for (ApexManager.ScanResult result : scanResults) {
             ApexInfo ai = result.apexInfo;
-
-            final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate(
-                    result.pkg, ai, flags);
-            if (packageInfo == null) {
-                throw new IllegalStateException("Unable to generate package info: "
-                        + ai.modulePath);
-            }
-            if (!packageInfo.packageName.equals(result.packageName)) {
+            String packageName = result.pkg.getPackageName();
+            if (!packageName.equals(result.packageName)) {
                 throw new IllegalStateException("Unmatched package name: "
-                        + result.packageName + " != " + packageInfo.packageName
+                        + result.packageName + " != " + packageName
                         + ", path=" + ai.modulePath);
             }
-            mAllPackagesCache.add(packageInfo);
+            mAllPackagesCache.add(Pair.create(ai, result.pkg));
             if (ai.isActive) {
-                if (!activePackagesSet.add(packageInfo.packageName)) {
+                if (!activePackagesSet.add(packageName)) {
                     throw new IllegalStateException(
-                            "Two active packages have the same name: "
-                                    + packageInfo.packageName);
+                            "Two active packages have the same name: " + packageName);
                 }
             }
             if (ai.isFactory) {
                 // Don't throw when the duplicating APEX is VNDK APEX
-                if (!factoryPackagesSet.add(packageInfo.packageName)
+                if (!factoryPackagesSet.add(packageName)
                         && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) {
                     throw new IllegalStateException(
-                            "Two factory packages have the same name: "
-                                    + packageInfo.packageName);
+                            "Two factory packages have the same name: " + packageName);
                 }
             }
         }
@@ -348,6 +331,13 @@
             if (throwable == null) {
                 // Calling hideAsFinal to assign derived fields for the app info flags.
                 parseResult.parsedPackage.hideAsFinal();
+
+                // TODO: When ENABLE_FEATURE_SCAN_APEX is finalized, remove this and the entire
+                //  calling path code
+                ScanPackageUtils.applyPolicy(parseResult.parsedPackage,
+                        PackageManagerService.SCAN_AS_SYSTEM,
+                        mPackageManager == null ? null : mPackageManager.getPlatformPackage(),
+                        false);
                 results.add(new ApexManager.ScanResult(
                         ai, parseResult.parsedPackage, parseResult.parsedPackage.getPackageName()));
             } else if (throwable instanceof PackageManagerException) {
@@ -363,34 +353,69 @@
     }
 
     /**
-     * Dump information about the packages contained in a particular cache
-     * @param packagesCache the cache to print information about.
-     * @param packageName a {@link String} containing a package name, or {@code null}. If set,
-     *                    only information about that specific package will be dumped.
-     * @param ipw the {@link IndentingPrintWriter} object to send information to.
+     * @see #dumpPackages(List, String, IndentingPrintWriter)
      */
-    static void dumpPackages(List<PackageInfo> packagesCache,
+    static void dumpPackageStates(List<PackageStateInternal> packageStates, boolean isActive,
             @Nullable String packageName, IndentingPrintWriter ipw) {
         ipw.println();
         ipw.increaseIndent();
-        for (int i = 0, size = packagesCache.size(); i < size; i++) {
-            final PackageInfo pi = packagesCache.get(i);
-            if (packageName != null && !packageName.equals(pi.packageName)) {
+        for (int i = 0, size = packageStates.size(); i < size; i++) {
+            final var packageState = packageStates.get(i);
+            var pkg = packageState.getPkg();
+            if (packageName != null && !packageName.equals(pkg.getPackageName())) {
                 continue;
             }
-            ipw.println(pi.packageName);
+            ipw.println(pkg.getPackageName());
             ipw.increaseIndent();
-            ipw.println("Version: " + pi.versionCode);
-            ipw.println("Path: " + pi.applicationInfo.sourceDir);
-            ipw.println("IsActive: " + isActive(pi));
-            ipw.println("IsFactory: " + isFactory(pi));
+            ipw.println("Version: " + pkg.getLongVersionCode());
+            ipw.println("Path: " + pkg.getBaseApkPath());
+            ipw.println("IsActive: " + isActive);
+            ipw.println("IsFactory: " + !packageState.isUpdatedSystemApp());
             ipw.println("ApplicationInfo: ");
             ipw.increaseIndent();
-            pi.applicationInfo.dump(new PrintWriterPrinter(ipw), "");
+            // TODO: Dump the package manually
+            AndroidPackageUtils.generateAppInfoWithoutState(pkg)
+                    .dump(new PrintWriterPrinter(ipw), "");
             ipw.decreaseIndent();
             ipw.decreaseIndent();
         }
         ipw.decreaseIndent();
         ipw.println();
     }
-}
+
+    /**
+     * Dump information about the packages contained in a particular cache
+     * @param packagesCache the cache to print information about.
+     * @param packageName a {@link String} containing a package name, or {@code null}. If set,
+     *                    only information about that specific package will be dumped.
+     * @param ipw the {@link IndentingPrintWriter} object to send information to.
+     */
+    static void dumpPackages(List<Pair<ApexInfo, AndroidPackage>> packagesCache,
+            @Nullable String packageName, IndentingPrintWriter ipw) {
+        ipw.println();
+        ipw.increaseIndent();
+        for (int i = 0, size = packagesCache.size(); i < size; i++) {
+            final var pair = packagesCache.get(i);
+            var apexInfo = pair.first;
+            var pkg = pair.second;
+            if (packageName != null && !packageName.equals(pkg.getPackageName())) {
+                continue;
+            }
+            ipw.println(pkg.getPackageName());
+            ipw.increaseIndent();
+            ipw.println("Version: " + pkg.getLongVersionCode());
+            ipw.println("Path: " + pkg.getBaseApkPath());
+            ipw.println("IsActive: " + apexInfo.isActive);
+            ipw.println("IsFactory: " + apexInfo.isFactory);
+            ipw.println("ApplicationInfo: ");
+            ipw.increaseIndent();
+            // TODO: Dump the package manually
+            AndroidPackageUtils.generateAppInfoWithoutState(pkg)
+                    .dump(new PrintWriterPrinter(ipw), "");
+            ipw.decreaseIndent();
+            ipw.decreaseIndent();
+        }
+        ipw.decreaseIndent();
+        ipw.println();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 63c25ea..8ec3d2b 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -65,6 +65,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.apex.ApexInfo;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -1018,11 +1019,12 @@
                 if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
                     apexFlags = ApexManager.MATCH_FACTORY_PACKAGE;
                 }
-                final PackageInfo pi = mApexPackageInfo.getPackageInfo(packageName, apexFlags);
-                if (pi == null) {
+                final var pair = mApexPackageInfo.getPackageInfo(packageName, apexFlags);
+                if (pair == null) {
                     return null;
                 }
-                return pi.applicationInfo;
+                return PackageInfoUtils.generateApplicationInfo(pair.second, flags,
+                        PackageUserStateInternal.DEFAULT, userId, null);
             }
         }
         if ("android".equals(packageName) || "system".equals(packageName)) {
@@ -1718,8 +1720,12 @@
             // Instant app filtering for APEX modules is ignored
             if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
                 if (matchApex) {
-                    return mApexPackageInfo.getPackageInfo(packageName,
+                    final var pair = mApexPackageInfo.getPackageInfo(packageName,
                             ApexManager.MATCH_FACTORY_PACKAGE);
+                    if (pair == null) {
+                        return null;
+                    }
+                    return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId);
                 }
             }
             final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName);
@@ -1775,8 +1781,12 @@
         }
         if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
             if (matchApex) {
-                return mApexPackageInfo.getPackageInfo(packageName,
+                final var pair = mApexPackageInfo.getPackageInfo(packageName,
                         ApexManager.MATCH_ACTIVE_PACKAGE);
+                if (pair == null) {
+                    return null;
+                }
+                return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId);
             }
         }
         return null;
@@ -1883,10 +1893,17 @@
         }
         if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
             if (listApex) {
+                List<Pair<ApexInfo, AndroidPackage>> pairs;
                 if (listFactory) {
-                    list.addAll(mApexPackageInfo.getFactoryPackages());
+                    pairs = mApexPackageInfo.getFactoryPackages();
                 } else {
-                    list.addAll(mApexPackageInfo.getActivePackages());
+                    pairs = mApexPackageInfo.getActivePackages();
+                }
+
+                for (int index = 0; index < pairs.size(); index++) {
+                    var pair = pairs.get(index);
+                    list.add(PackageInfoUtils.generate(pair.second, pair.first, flags, null,
+                            userId));
                 }
             }
         }
@@ -3404,29 +3421,23 @@
         } // switch
     }
 
-    private void generateApexPackageInfo(List<PackageInfo> activePackages,
-            List<PackageInfo> inactivePackages, List<PackageInfo> factoryPackages) {
+    private void generateApexPackageInfo(@NonNull List<PackageStateInternal> activePackages,
+            @NonNull List<PackageStateInternal> inactivePackages,
+            @NonNull List<PackageStateInternal> factoryActivePackages,
+            @NonNull List<PackageStateInternal> factoryInactivePackages) {
         for (AndroidPackage p : mPackages.values()) {
             final String packageName = p.getPackageName();
             PackageStateInternal ps = mSettings.getPackage(packageName);
             if (!p.isApex() || ps == null) {
                 continue;
             }
-            PackageInfo pi = generatePackageInfo(ps, 0, 0);
-            if (pi == null) {
-                continue;
-            }
-            pi.isActiveApex = true;
-            activePackages.add(pi);
+            activePackages.add(ps);
             if (!ps.isUpdatedSystemApp()) {
-                factoryPackages.add(pi);
+                factoryActivePackages.add(ps);
             } else {
                 PackageStateInternal psDisabled = mSettings.getDisabledSystemPkg(packageName);
-                pi = generatePackageInfo(psDisabled, 0, 0);
-                if (pi != null) {
-                    factoryPackages.add(pi);
-                    inactivePackages.add(pi);
-                }
+                factoryInactivePackages.add(psDisabled);
+                inactivePackages.add(psDisabled);
             }
         }
     }
@@ -3434,16 +3445,19 @@
     private void dumpApex(PrintWriter pw, String packageName) {
         if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) {
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
-            List<PackageInfo> activePackages = new ArrayList<>();
-            List<PackageInfo> inactivePackages = new ArrayList<>();
-            List<PackageInfo> factoryPackages = new ArrayList<>();
-            generateApexPackageInfo(activePackages, inactivePackages, factoryPackages);
+            List<PackageStateInternal> activePackages = new ArrayList<>();
+            List<PackageStateInternal> inactivePackages = new ArrayList<>();
+            List<PackageStateInternal> factoryActivePackages = new ArrayList<>();
+            List<PackageStateInternal> factoryInactivePackages = new ArrayList<>();
+            generateApexPackageInfo(activePackages, inactivePackages, factoryActivePackages,
+                    factoryInactivePackages);
             ipw.println("Active APEX packages:");
-            ApexPackageInfo.dumpPackages(activePackages, packageName, ipw);
+            ApexPackageInfo.dumpPackageStates(activePackages, true, packageName, ipw);
             ipw.println("Inactive APEX packages:");
-            ApexPackageInfo.dumpPackages(inactivePackages, packageName, ipw);
+            ApexPackageInfo.dumpPackageStates(inactivePackages, false, packageName, ipw);
             ipw.println("Factory APEX packages:");
-            ApexPackageInfo.dumpPackages(factoryPackages, packageName, ipw);
+            ApexPackageInfo.dumpPackageStates(factoryActivePackages, true, packageName, ipw);
+            ApexPackageInfo.dumpPackageStates(factoryInactivePackages, false, packageName, ipw);
         } else {
             mApexPackageInfo.dump(pw, packageName);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2bdf62bd..32b3e6a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1611,7 +1611,7 @@
         mSharedLibraries = injector.getSharedLibrariesImpl();
 
         mApexManager = testParams.apexManager;
-        mApexPackageInfo = new ApexPackageInfo();
+        mApexPackageInfo = new ApexPackageInfo(this);
         mArtManagerService = testParams.artManagerService;
         mAvailableFeatures = testParams.availableFeatures;
         mBackgroundDexOptService = testParams.backgroundDexOptService;
@@ -1811,7 +1811,7 @@
         mProtectedPackages = new ProtectedPackages(mContext);
 
         mApexManager = injector.getApexManager();
-        mApexPackageInfo = new ApexPackageInfo();
+        mApexPackageInfo = new ApexPackageInfo(this);
         mAppsFilter = mInjector.getAppsFilter();
 
         mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 0e57c91..86affdd 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -823,8 +823,8 @@
      * ideally be static, but, it requires locks to read system state.
      */
     public static void applyPolicy(ParsedPackage parsedPackage,
-            final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
-            boolean isUpdatedSystemApp) {
+            final @PackageManagerService.ScanFlags int scanFlags,
+            @Nullable AndroidPackage platformPkg, boolean isUpdatedSystemApp) {
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
             parsedPackage.setSystem(true);
             // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 937b2cf..b57d4d5 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -219,38 +219,21 @@
 
         // Install/uninstall system packages per user.
         for (int userId : mUm.getUserIds()) {
-            final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
+            final Set<String> userAllowlist = getInstallablePackagesForUserId(userId);
 
-            // If null, run for all packages
-            if (userWhitelist == null) {
-                pmInt.forEachPackageState(packageState -> {
-                    if (packageState.getPkg() == null) {
-                        return;
-                    }
-                    final boolean install = !packageState.getTransientState()
-                            .isHiddenUntilInstalled();
-                    if (packageState.getUserStateOrDefault(userId).isInstalled() != install
-                            && shouldChangeInstallationState(packageState, install, userId,
-                            isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
-                        changesToCommit.add(userId, packageState.getPackageName(), install);
-                    }
-                });
-            } else {
-                for (String packageName : userWhitelist) {
-                    PackageStateInternal packageState = pmInt.getPackageStateInternal(packageName);
-                    if (packageState.getPkg() == null) {
-                        continue;
-                    }
-
-                    final boolean install = !packageState.getTransientState()
-                            .isHiddenUntilInstalled();
-                    if (packageState.getUserStateOrDefault(userId).isInstalled() != install
-                            && shouldChangeInstallationState(packageState, install, userId,
-                            isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
-                        changesToCommit.add(userId, packageState.getPackageName(), install);
-                    }
+            pmInt.forEachPackageState(packageState -> {
+                if (packageState.getPkg() == null) {
+                    return;
                 }
-            }
+                boolean install = (userAllowlist == null
+                                || userAllowlist.contains(packageState.getPackageName()))
+                        && !packageState.getTransientState().isHiddenUntilInstalled();
+                if (packageState.getUserStateOrDefault(userId).isInstalled() != install
+                        && shouldChangeInstallationState(packageState, install, userId,
+                        isFirstBoot, isConsideredUpgrade, preExistingPackages)) {
+                    changesToCommit.add(userId, packageState.getPackageName(), install);
+                }
+            });
         }
 
         pmInt.commitPackageStateMutation(null, packageStateMutator -> {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 9c620c4..a44def8 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -93,12 +93,14 @@
 
     /**
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+     * @deprecated Once ENABLE_FEATURE_SCAN_APEX is removed, this should also be removed.
      */
+    @Deprecated
     @Nullable
-    public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, int flags,
-            @Nullable PackageStateInternal pkgSetting) {
+    public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, long flags,
+            @Nullable PackageStateInternal pkgSetting, @UserIdInt int userId) {
         return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
-                PackageUserStateInternal.DEFAULT, UserHandle.getCallingUserId(), apexInfo, pkgSetting);
+                PackageUserStateInternal.DEFAULT, userId, apexInfo, pkgSetting);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index b91f15a..748d328 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,7 +22,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import com.android.server.pm.pkg.SELinuxUtil;
 import android.content.pm.SigningDetails;
 import android.content.res.TypedArray;
 import android.os.Environment;
@@ -35,6 +34,7 @@
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.pkg.SELinuxUtil;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedProvider;
@@ -255,7 +255,7 @@
     }
 
     @Override
-    public PackageImpl setSigningDetails(@Nullable SigningDetails value) {
+    public PackageImpl setSigningDetails(@NonNull SigningDetails value) {
         super.setSigningDetails(value);
         return this;
     }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 40f859c..b7b37b2 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -334,7 +334,7 @@
 
     ParsingPackage setSharedUserLabel(int sharedUserLabel);
 
-    ParsingPackage setSigningDetails(SigningDetails signingDetails);
+    ParsingPackage setSigningDetails(@NonNull SigningDetails signingDetails);
 
     ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 6a4513d..803780f 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -317,8 +317,8 @@
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     protected String volumeUuid;
-    @Nullable
-    private SigningDetails signingDetails;
+    @NonNull
+    private SigningDetails signingDetails = SigningDetails.UNKNOWN;
 
     @NonNull
     @DataClass.ParcelWith(ForInternedString.class)
@@ -1873,7 +1873,7 @@
         return volumeUuid;
     }
 
-    @Nullable
+    @NonNull
     @Override
     public SigningDetails getSigningDetails() {
         return signingDetails;
@@ -2474,7 +2474,7 @@
     }
 
     @Override
-    public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
+    public ParsingPackageImpl setSigningDetails(@NonNull SigningDetails value) {
         signingDetails = value;
         return this;
     }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
index 20b1ed8..2272999 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java
@@ -188,6 +188,7 @@
      * The signature data of all APKs in this package, which must be exactly the same across the
      * base and splits.
      */
+    @NonNull
     SigningDetails getSigningDetails();
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 69bbff3..42e0533 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -272,7 +272,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConstrainDisplayApisConfig;
 import android.content.pm.PackageManager;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -478,7 +477,6 @@
     private int mLastReportedDisplayId;
     boolean mLastReportedMultiWindowMode;
     boolean mLastReportedPictureInPictureMode;
-    CompatibilityInfo compat;// last used compatibility mode
     ActivityRecord resultTo; // who started this entry, so will get our reply
     final String resultWho; // additional identifier for use by resultTo.
     final int requestCode;  // code given by requester (resultTo)
@@ -1022,7 +1020,8 @@
         if (rootVoiceInteraction) {
             pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
         }
-        pw.print(prefix); pw.print("compat="); pw.print(compat);
+        pw.print(prefix); pw.print("compat=");
+        pw.print(mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo));
                 pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
                 pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
                 pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8474068..345ec11 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -875,7 +875,6 @@
                         r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
                 r.forceNewConfig = false;
                 mService.getAppWarningsLocked().onStartActivity(r);
-                r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
 
                 // Because we could be starting an Activity in the system process this may not go
                 // across a Binder interface which would create a new Configuration. Consequently
@@ -905,7 +904,7 @@
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
-                        mergedConfiguration.getOverrideConfiguration(), r.compat,
+                        mergedConfiguration.getOverrideConfiguration(),
                         r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
                         proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
                         results, newIntents, r.takeOptions(), isTransitionForward,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3ef4aae..366ca3b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -137,7 +137,6 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.WINDOW;
@@ -4957,10 +4956,6 @@
                 || isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
     }
 
-    boolean isExitAnimationRunningSelfOrChild() {
-        return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
-    }
-
     private boolean shouldFinishAnimatingExit() {
         // Exit animation might be applied soon.
         if (inTransition()) {
@@ -5916,6 +5911,10 @@
         if (!super.prepareSync()) {
             return false;
         }
+        if (mIsWallpaper) {
+            // TODO(b/233286785): Add sync support to wallpaper.
+            return false;
+        }
         // In the WindowContainer implementation we immediately mark ready
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index 721777c..13510adb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -30,6 +30,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArrayMap;
+import android.util.SparseLongArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -174,10 +175,13 @@
         when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit);
 
         Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, 2000, 0));
+        ledger.recordTransaction(
+                new Ledger.Transaction(0, 1000L, EconomicPolicy.TYPE_REWARD | 1, null, 2000, 0));
         // Negative ledger balance shouldn't affect the total circulation value.
         ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE);
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
+        ledger.recordTransaction(
+                new Ledger.Transaction(0, 1000L,
+                        EconomicPolicy.TYPE_ACTION | 1, null, -5000, 3000));
         mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
         mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
         mScribeUnderTest.adjustRemainingConsumableCakesLocked(
@@ -209,9 +213,13 @@
     @Test
     public void testWritingPopulatedLedgerToDisk() {
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, -1));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 12));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(1500, 2000,
+                        EconomicPolicy.TYPE_REWARD | 2, "green", 52, -1));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 12));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         mScribeUnderTest.loadFromDiskLocked();
@@ -230,11 +238,13 @@
                 addInstalledPackage(userId, pkgName);
                 final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName);
                 ledger.recordTransaction(new Ledger.Transaction(
-                        0, 1000L * u + l, 1, null, -51L * u + l, 50));
+                        0, 1000L * u + l, EconomicPolicy.TYPE_ACTION | 1, null, -51L * u + l, 50));
                 ledger.recordTransaction(new Ledger.Transaction(
-                        1500L * u + l, 2000L * u + l, 2 * u + l, "green" + u + l, 52L * u + l, 0));
+                        1500L * u + l, 2000L * u + l,
+                        EconomicPolicy.TYPE_REWARD | 2 * u + l, "green" + u + l, 52L * u + l, 0));
                 ledger.recordTransaction(new Ledger.Transaction(
-                        2500L * u + l, 3000L * u + l, 3 * u + l, "blue" + u + l, 3L * u + l, 0));
+                        2500L * u + l, 3000L * u + l,
+                        EconomicPolicy.TYPE_REWARD | 3 * u + l, "blue" + u + l, 3L * u + l, 0));
                 ledgers.add(userId, pkgName, ledger);
             }
         }
@@ -248,9 +258,12 @@
     @Test
     public void testDiscardLedgerFromDisk() {
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 0));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 1));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 1));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 0));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 1));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         mScribeUnderTest.loadFromDiskLocked();
@@ -269,9 +282,12 @@
     public void testLoadingMissingPackageFromDisk() {
         final String pkgName = TEST_PACKAGE + ".uninstalled";
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 1));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 2));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION | 1, null, 51, 1));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 2));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_ACTION | 3, "blue", -3, 3));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         // Package isn't installed, so make sure it's not saved to memory after loading.
@@ -283,9 +299,13 @@
     public void testLoadingMissingUserFromDisk() {
         final int userId = TEST_USER_ID + 1;
         final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE);
-        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51, 0));
-        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52, 1));
-        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3, 3));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 1));
+        ogLedger.recordTransaction(
+                new Ledger.Transaction(2500, 3000,
+                        EconomicPolicy.TYPE_REGULATION | 3, "blue", 3, 3));
         mScribeUnderTest.writeImmediatelyForTesting();
 
         // User doesn't show up with any packages, so make sure nothing is saved after loading.
@@ -331,12 +351,34 @@
         }
         assertNotNull(actual);
         assertEquals(expected.getCurrentBalance(), actual.getCurrentBalance());
+
         List<Ledger.Transaction> expectedTransactions = expected.getTransactions();
         List<Ledger.Transaction> actualTransactions = actual.getTransactions();
         assertEquals(expectedTransactions.size(), actualTransactions.size());
         for (int i = 0; i < expectedTransactions.size(); ++i) {
             assertTransactionsEqual(expectedTransactions.get(i), actualTransactions.get(i));
         }
+
+        List<Ledger.RewardBucket> expectedRewardBuckets = expected.getRewardBuckets();
+        List<Ledger.RewardBucket> actualRewardBuckets = actual.getRewardBuckets();
+        assertEquals(expectedRewardBuckets.size(), actualRewardBuckets.size());
+        for (int i = 0; i < expectedRewardBuckets.size(); ++i) {
+            assertRewardBucketsEqual(expectedRewardBuckets.get(i), actualRewardBuckets.get(i));
+        }
+    }
+
+    private void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) {
+        if (expected == null) {
+            assertNull(actual);
+            return;
+        }
+        assertNotNull(actual);
+        final int size = expected.size();
+        assertEquals(size, actual.size());
+        for (int i = 0; i < size; ++i) {
+            assertEquals(expected.keyAt(i), actual.keyAt(i));
+            assertEquals(expected.valueAt(i), actual.valueAt(i));
+        }
     }
 
     private void assertReportListsEqual(List<Analyst.Report> expected,
@@ -382,6 +424,17 @@
         }
     }
 
+    private void assertRewardBucketsEqual(Ledger.RewardBucket expected,
+            Ledger.RewardBucket actual) {
+        if (expected == null) {
+            assertNull(actual);
+            return;
+        }
+        assertNotNull(actual);
+        assertEquals(expected.startTimeMs, actual.startTimeMs);
+        assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta);
+    }
+
     private void assertTransactionsEqual(Ledger.Transaction expected, Ledger.Transaction actual) {
         if (expected == null) {
             assertNull(actual);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 20482af..503ca69 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -36,8 +36,6 @@
 import android.apex.ApexSessionParams;
 import android.apex.IApexService;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -93,16 +91,16 @@
         ApexPackageInfo apexPackageInfo = new ApexPackageInfo();
         apexPackageInfo.scanApexPackages(
                 apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
-        final PackageInfo activePkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
+        final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_ACTIVE_PACKAGE);
 
-        assertThat(activePkgPi).isNotNull();
-        assertThat(activePkgPi.packageName).contains(TEST_APEX_PKG);
+        assertThat(activePair).isNotNull();
+        assertThat(activePair.second.getPackageName()).contains(TEST_APEX_PKG);
 
-        final PackageInfo factoryPkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
+        final var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_FACTORY_PACKAGE);
 
-        assertThat(factoryPkgPi).isNull();
+        assertThat(factoryPair).isNull();
     }
 
     @Test
@@ -111,16 +109,16 @@
         ApexPackageInfo apexPackageInfo = new ApexPackageInfo();
         apexPackageInfo.scanApexPackages(
                 apexInfo, mPackageParser2, ParallelPackageParser.makeExecutorService());
-        PackageInfo factoryPkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
+        var factoryPair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_FACTORY_PACKAGE);
 
-        assertThat(factoryPkgPi).isNotNull();
-        assertThat(factoryPkgPi.packageName).contains(TEST_APEX_PKG);
+        assertThat(factoryPair).isNotNull();
+        assertThat(factoryPair.second.getPackageName()).contains(TEST_APEX_PKG);
 
-        final PackageInfo activePkgPi = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
+        final var activePair = apexPackageInfo.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_ACTIVE_PACKAGE);
 
-        assertThat(activePkgPi).isNull();
+        assertThat(activePair).isNull();
     }
 
     @Test
@@ -388,23 +386,16 @@
         newApexInfo = mApexManager.installPackage(installedApex);
         apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2);
 
-        PackageInfo newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
+        var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
                 ApexManager.MATCH_ACTIVE_PACKAGE);
-        assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath());
-        assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
-                .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
-            .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
+        assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
+        assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2);
 
-        PackageInfo factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
+        var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
                 ApexManager.MATCH_FACTORY_PACKAGE);
-        assertThat(factoryInfo.applicationInfo.sourceDir).isEqualTo(activeApexInfo.modulePath);
-        assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
-            .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
-                .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
+        assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath);
+        assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1);
+        assertThat(factoryInfo.second.isSystem()).isTrue();
     }
 
     @Test
@@ -429,23 +420,16 @@
         newApexInfo = mApexManager.installPackage(installedApex);
         apexPackageInfo.notifyPackageInstalled(newApexInfo, mPackageParser2);
 
-        PackageInfo newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
+        var newInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
                 ApexManager.MATCH_ACTIVE_PACKAGE);
-        assertThat(newInfo.applicationInfo.sourceDir).isEqualTo(finalApex.getAbsolutePath());
-        assertThat(newInfo.applicationInfo.longVersionCode).isEqualTo(2);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
-                .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
-        assertThat(newInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
-            .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
+        assertThat(newInfo.second.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
+        assertThat(newInfo.second.getLongVersionCode()).isEqualTo(2);
 
-        PackageInfo factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
+        var factoryInfo = apexPackageInfo.getPackageInfo("test.apex.rebootless",
                 ApexManager.MATCH_FACTORY_PACKAGE);
-        assertThat(factoryInfo.applicationInfo.sourceDir).isEqualTo(factoryApexInfo.modulePath);
-        assertThat(factoryInfo.applicationInfo.longVersionCode).isEqualTo(1);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
-            .isEqualTo(ApplicationInfo.FLAG_SYSTEM);
-        assertThat(factoryInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
-                .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
+        assertThat(factoryInfo.second.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath);
+        assertThat(factoryInfo.second.getLongVersionCode()).isEqualTo(1);
+        assertThat(factoryInfo.second.isSystem()).isTrue();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
index 22dcf84..54566c3 100644
--- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -22,7 +22,12 @@
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.util.SparseLongArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,7 +37,10 @@
 import org.junit.runner.RunWith;
 
 import java.time.Clock;
+import java.time.Duration;
 import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.List;
 
 /** Test that the ledger records transactions correctly. */
 @RunWith(AndroidJUnit4.class)
@@ -44,6 +52,11 @@
         TareUtils.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
     }
 
+    private void shiftSystemTime(long incrementMs) {
+        TareUtils.sSystemClock =
+                Clock.offset(TareUtils.sSystemClock, Duration.ofMillis(incrementMs));
+    }
+
     @Test
     public void testInitialState() {
         final Ledger ledger = new Ledger();
@@ -52,37 +65,149 @@
     }
 
     @Test
+    public void testInitialization_FullLists() {
+        final long balance = 1234567890L;
+        List<Ledger.Transaction> transactions = new ArrayList<>();
+        List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>();
+
+        final long now = getCurrentTimeMillis();
+        Ledger.Transaction secondTxn = null;
+        Ledger.RewardBucket remainingBucket = null;
+        for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT; ++i) {
+            final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS;
+            Ledger.Transaction transaction = new Ledger.Transaction(
+                    start, start + MINUTE_IN_MILLIS, 1, null, 400, 0);
+            if (i == 1) {
+                secondTxn = transaction;
+            }
+            transactions.add(transaction);
+        }
+        for (int b = 0; b < Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) {
+            final long start = now - (Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 24 * HOUR_IN_MILLIS;
+            Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket();
+            rewardBucket.startTimeMs = start;
+            for (int r = 0; r < 5; ++r) {
+                rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r);
+            }
+            if (b == Ledger.NUM_REWARD_BUCKET_WINDOWS - 1) {
+                remainingBucket = rewardBucket;
+            }
+            rewardBuckets.add(rewardBucket);
+        }
+        final Ledger ledger = new Ledger(balance, transactions, rewardBuckets);
+        assertEquals(balance, ledger.getCurrentBalance());
+        assertEquals(transactions, ledger.getTransactions());
+        // Everything but the last bucket is old, so the returned list should only contain that
+        // bucket.
+        rewardBuckets.clear();
+        rewardBuckets.add(remainingBucket);
+        assertEquals(rewardBuckets, ledger.getRewardBuckets());
+
+        // Make sure the ledger can properly record new transactions.
+        final long start = now - MINUTE_IN_MILLIS;
+        final long delta = 400;
+        final Ledger.Transaction transaction = new Ledger.Transaction(
+                start, start + MINUTE_IN_MILLIS, EconomicPolicy.TYPE_REWARD | 1, null, delta, 0);
+        ledger.recordTransaction(transaction);
+        assertEquals(balance + delta, ledger.getCurrentBalance());
+        transactions = ledger.getTransactions();
+        assertEquals(secondTxn, transactions.get(0));
+        assertEquals(transaction, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1));
+        final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket();
+        rewardBucket.startTimeMs = now;
+        rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | 1, delta);
+        rewardBuckets = ledger.getRewardBuckets();
+        assertRewardBucketsEqual(remainingBucket, rewardBuckets.get(0));
+        assertRewardBucketsEqual(rewardBucket, rewardBuckets.get(1));
+    }
+
+    @Test
+    public void testInitialization_OverflowingLists() {
+        final long balance = 1234567890L;
+        final List<Ledger.Transaction> transactions = new ArrayList<>();
+        final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>();
+
+        final long now = getCurrentTimeMillis();
+        for (int i = 0; i < 2 * Ledger.MAX_TRANSACTION_COUNT; ++i) {
+            final long start = now - 20 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS;
+            Ledger.Transaction transaction = new Ledger.Transaction(
+                    start, start + MINUTE_IN_MILLIS, 1, null, 400, 0);
+            transactions.add(transaction);
+        }
+        for (int b = 0; b < 2 * Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) {
+            final long start = now
+                    - (2 * Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 6 * HOUR_IN_MILLIS;
+            Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket();
+            rewardBucket.startTimeMs = start;
+            for (int r = 0; r < 5; ++r) {
+                rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r);
+            }
+            rewardBuckets.add(rewardBucket);
+        }
+        final Ledger ledger = new Ledger(balance, transactions, rewardBuckets);
+        assertEquals(balance, ledger.getCurrentBalance());
+        assertEquals(transactions.subList(Ledger.MAX_TRANSACTION_COUNT,
+                        2 * Ledger.MAX_TRANSACTION_COUNT),
+                ledger.getTransactions());
+        assertEquals(rewardBuckets.subList(Ledger.NUM_REWARD_BUCKET_WINDOWS,
+                        2 * Ledger.NUM_REWARD_BUCKET_WINDOWS),
+                ledger.getRewardBuckets());
+    }
+
+    @Test
     public void testMultipleTransactions() {
         final Ledger ledger = new Ledger();
         ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0));
         assertEquals(5, ledger.getCurrentBalance());
-        assertEquals(5, ledger.get24HourSum(1, 60_000));
         ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0));
         assertEquals(30, ledger.getCurrentBalance());
-        assertEquals(30, ledger.get24HourSum(1, 60_000));
         ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5));
         assertEquals(20, ledger.getCurrentBalance());
-        assertEquals(20, ledger.get24HourSum(1, 60_000));
     }
 
     @Test
     public void test24HourSum() {
+        final long now = getCurrentTimeMillis();
+        final long end = now + 24 * HOUR_IN_MILLIS;
+        final int reward1 = EconomicPolicy.TYPE_REWARD | 1;
+        final int reward2 = EconomicPolicy.TYPE_REWARD | 2;
         final Ledger ledger = new Ledger();
-        ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500, 0));
-        assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+
+        // First bucket
+        assertEquals(0, ledger.get24HourSum(reward1, end));
+        ledger.recordTransaction(new Ledger.Transaction(now, now + 1000, reward1, null, 500, 0));
+        assertEquals(500, ledger.get24HourSum(reward1, end));
+        assertEquals(0, ledger.get24HourSum(reward2, end));
         ledger.recordTransaction(
-                new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500, 0));
-        assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+                new Ledger.Transaction(now + 2 * HOUR_IN_MILLIS, now + 3 * HOUR_IN_MILLIS,
+                        reward1, null, 2500, 0));
+        assertEquals(3000, ledger.get24HourSum(reward1, end));
+        // Second bucket
+        shiftSystemTime(7 * HOUR_IN_MILLIS); // now + 7
         ledger.recordTransaction(
-                new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1, 0));
-        assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
-        assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
-        assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
-        // Pro-rated as the second transaction phases out
-        assertEquals(1251,
-                ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS));
-        assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS));
-        assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS));
+                new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS,
+                        reward1, null, 1, 0));
+        ledger.recordTransaction(
+                new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS,
+                        reward2, null, 42, 0));
+        assertEquals(3001, ledger.get24HourSum(reward1, end));
+        assertEquals(42, ledger.get24HourSum(reward2, end));
+        // Third bucket
+        shiftSystemTime(12 * HOUR_IN_MILLIS); // now + 19
+        ledger.recordTransaction(
+                new Ledger.Transaction(now + 12 * HOUR_IN_MILLIS, now + 13 * HOUR_IN_MILLIS,
+                        reward1, null, 300, 0));
+        assertEquals(3301, ledger.get24HourSum(reward1, end));
+        assertRewardBucketsInOrder(ledger.getRewardBuckets());
+        // Older buckets should be excluded
+        assertEquals(301, ledger.get24HourSum(reward1, end + HOUR_IN_MILLIS));
+        assertEquals(301, ledger.get24HourSum(reward1, end + 2 * HOUR_IN_MILLIS));
+        // 2nd bucket should still be included since it started at the 7 hour mark
+        assertEquals(301, ledger.get24HourSum(reward1, end + 6 * HOUR_IN_MILLIS));
+        assertEquals(42, ledger.get24HourSum(reward2, end + 6 * HOUR_IN_MILLIS));
+        assertEquals(300, ledger.get24HourSum(reward1, end + 7 * HOUR_IN_MILLIS + 1));
+        assertEquals(0, ledger.get24HourSum(reward2, end + 8 * HOUR_IN_MILLIS));
+        assertEquals(0, ledger.get24HourSum(reward1, end + 19 * HOUR_IN_MILLIS + 1));
     }
 
     @Test
@@ -125,4 +250,127 @@
         ledger.removeOldTransactions(0);
         assertNull(ledger.getEarliestTransaction());
     }
+
+    @Test
+    public void testTransactionsAlwaysInOrder() {
+        final Ledger ledger = new Ledger();
+        List<Ledger.Transaction> transactions = ledger.getTransactions();
+        assertTrue(transactions.isEmpty());
+
+        final long now = getCurrentTimeMillis();
+        Ledger.Transaction transaction1 = new Ledger.Transaction(
+                now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0);
+        Ledger.Transaction transaction2 = new Ledger.Transaction(
+                now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0);
+        Ledger.Transaction transaction3 = new Ledger.Transaction(
+                now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0);
+        // Instant event
+        Ledger.Transaction transaction4 = new Ledger.Transaction(
+                now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0);
+
+        Ledger.Transaction transaction5 = new Ledger.Transaction(
+                now - 15 * HOUR_IN_MILLIS, now - 15 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS,
+                1, null, 400, 0);
+        ledger.recordTransaction(transaction1);
+        ledger.recordTransaction(transaction2);
+        ledger.recordTransaction(transaction3);
+        ledger.recordTransaction(transaction4);
+        ledger.recordTransaction(transaction5);
+
+        transactions = ledger.getTransactions();
+        assertEquals(5, transactions.size());
+        assertTransactionsInOrder(transactions);
+
+        for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT - 5; ++i) {
+            final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS;
+            Ledger.Transaction transaction = new Ledger.Transaction(
+                    start, start + MINUTE_IN_MILLIS, 1, null, 400, 0);
+            ledger.recordTransaction(transaction);
+        }
+        transactions = ledger.getTransactions();
+        assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size());
+        assertTransactionsInOrder(transactions);
+
+        long start = now - 5 * HOUR_IN_MILLIS;
+        Ledger.Transaction transactionLast5 = new Ledger.Transaction(
+                start, start + MINUTE_IN_MILLIS, 1, null, 4800, 0);
+        start = now - 4 * HOUR_IN_MILLIS;
+        Ledger.Transaction transactionLast4 = new Ledger.Transaction(
+                start, start + MINUTE_IN_MILLIS, 1, null, 600, 0);
+        start = now - 3 * HOUR_IN_MILLIS;
+        Ledger.Transaction transactionLast3 = new Ledger.Transaction(
+                start, start + MINUTE_IN_MILLIS, 1, null, 600, 0);
+        // Instant event
+        start = now - 2 * HOUR_IN_MILLIS;
+        Ledger.Transaction transactionLast2 = new Ledger.Transaction(
+                start, start, 1, null, 500, 0);
+        Ledger.Transaction transactionLast1 = new Ledger.Transaction(
+                start, start + MINUTE_IN_MILLIS, 1, null, 400, 0);
+        ledger.recordTransaction(transactionLast5);
+        ledger.recordTransaction(transactionLast4);
+        ledger.recordTransaction(transactionLast3);
+        ledger.recordTransaction(transactionLast2);
+        ledger.recordTransaction(transactionLast1);
+
+        transactions = ledger.getTransactions();
+        assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size());
+        assertTransactionsInOrder(transactions);
+        assertEquals(transactionLast1, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1));
+        assertEquals(transactionLast2, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 2));
+        assertEquals(transactionLast3, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 3));
+        assertEquals(transactionLast4, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 4));
+        assertEquals(transactionLast5, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 5));
+        assertFalse(transactions.contains(transaction1));
+        assertFalse(transactions.contains(transaction2));
+        assertFalse(transactions.contains(transaction3));
+        assertFalse(transactions.contains(transaction4));
+        assertFalse(transactions.contains(transaction5));
+    }
+
+    private void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) {
+        if (expected == null) {
+            assertNull(actual);
+            return;
+        }
+        assertNotNull(actual);
+        final int size = expected.size();
+        assertEquals(size, actual.size());
+        for (int i = 0; i < size; ++i) {
+            assertEquals(expected.keyAt(i), actual.keyAt(i));
+            assertEquals(expected.valueAt(i), actual.valueAt(i));
+        }
+    }
+
+    private void assertRewardBucketsEqual(Ledger.RewardBucket expected,
+            Ledger.RewardBucket actual) {
+        if (expected == null) {
+            assertNull(actual);
+            return;
+        }
+        assertNotNull(actual);
+        assertEquals(expected.startTimeMs, actual.startTimeMs);
+        assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta);
+    }
+
+    private void assertRewardBucketsInOrder(List<Ledger.RewardBucket> rewardBuckets) {
+        assertNotNull(rewardBuckets);
+        for (int i = 1; i < rewardBuckets.size(); ++i) {
+            final Ledger.RewardBucket prev = rewardBuckets.get(i - 1);
+            final Ledger.RewardBucket cur = rewardBuckets.get(i);
+            assertTrue("Newer bucket stored before older bucket @ index " + i
+                            + ": " + prev.startTimeMs + " vs " + cur.startTimeMs,
+                    prev.startTimeMs <= cur.startTimeMs);
+        }
+    }
+
+    private void assertTransactionsInOrder(List<Ledger.Transaction> transactions) {
+        assertNotNull(transactions);
+        for (int i = 1; i < transactions.size(); ++i) {
+            final Ledger.Transaction prev = transactions.get(i - 1);
+            final Ledger.Transaction cur = transactions.get(i);
+            assertTrue("Newer transaction stored before older transaction @ index " + i
+                            + ": " + prev.endTimeMs + " vs " + cur.endTimeMs,
+                    prev.endTimeMs <= cur.endTimeMs);
+        }
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index a0b01a7..959429e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -639,6 +639,11 @@
         mAppStandby.initializeDefaultsForSystemApps(userId);
     }
 
+    private boolean sameApp(int callingUid, @UserIdInt int userId, String packageName) {
+        return mPackageManagerInternal.getPackageUid(packageName, 0 /* flags */, userId)
+                == callingUid;
+    }
+
     private boolean isInstantApp(String packageName, int userId) {
         return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
     }
@@ -2354,17 +2359,15 @@
             }
             final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             // If the calling app is asking about itself, continue, else check for permission.
-            if (packageUid != callingUid) {
-                if (!hasPermission(callingPackage)) {
-                    throw new SecurityException(
-                            "Don't have permission to query app standby bucket");
-                }
+            final boolean sameApp = packageUid == callingUid;
+            if (!sameApp && !hasPermission(callingPackage)) {
+                throw new SecurityException("Don't have permission to query app standby bucket");
             }
 
             final boolean isInstantApp = isInstantApp(packageName, userId);
             final boolean cannotAccessInstantApps = shouldObfuscateInstantAppsForCaller(callingUid,
                     userId);
-            if (packageUid < 0 || (isInstantApp && cannotAccessInstantApps)) {
+            if (packageUid < 0 || (!sameApp && isInstantApp && cannotAccessInstantApps)) {
                 throw new IllegalArgumentException(
                         "Cannot get standby bucket for non existent package (" + packageName + ")");
             }
@@ -2418,7 +2421,9 @@
                 }
                 final int targetUserId = userId;
                 standbyBucketList.removeIf(
-                        i -> cannotAccessInstantApps && isInstantApp(i.mPackageName, targetUserId));
+                        i -> !sameApp(callingUid, targetUserId, i.mPackageName)
+                                && isInstantApp(i.mPackageName, targetUserId)
+                                && cannotAccessInstantApps);
                 return new ParceledListSlice<>(standbyBucketList);
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/tests/Camera2Tests/CameraToo/Android.bp b/tests/Camera2Tests/CameraToo/Android.bp
new file mode 100644
index 0000000..ebc6fed
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2014 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+android_test {
+    name: "CameraToo",
+
+    sdk_version: "current",
+    srcs: ["src/**/*.java"],
+
+}
diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk
deleted file mode 100644
index 3347314..0000000
--- a/tests/Camera2Tests/CameraToo/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := CameraToo
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-include $(BUILD_PACKAGE)
diff --git a/tests/backup/Android.bp b/tests/backup/Android.bp
new file mode 100644
index 0000000..3890a1f
--- /dev/null
+++ b/tests/backup/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 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.
+
+// native test
+// ========================================
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "frameworks_base_license",
+    ],
+}
+
+cc_binary {
+    name: "backup_helper_test",
+
+    srcs: ["backup_helper_test.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libandroidfw",
+        "libutils",
+    ],
+
+}
+
+// java test
+// ========================================
+android_app {
+    name: "BackupTest",
+
+    srcs: ["**/*.java"],
+
+    platform_apis: true,
+
+    optimize: {
+        enabled: false,
+    },
+
+}
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
deleted file mode 100644
index b6f3471..0000000
--- a/tests/backup/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2008 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# native test
-# ========================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    backup_helper_test.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := backup_helper_test
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SHARED_LIBRARIES := libandroidfw libutils
-
-include $(BUILD_EXECUTABLE)
-
-# java test
-# ========================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BackupTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)