Merge "Import translations. DO NOT MERGE ANYWHERE"
diff --git a/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java
new file mode 100644
index 0000000..b93a6ea
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.BiConsumer;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArrayMapPerfTest {
+    private static final int NUM_ITERATIONS = 100;
+    private static final int SET_SIZE_SMALL = 10;
+    private static final int SET_SIZE_LARGE = 50;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testForEach_Small() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BiConsumer<String, Integer> consumer = (s, i) -> {
+        };
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArrayMap<String, Integer> map = new ArrayMap<>();
+                for (int j = 0; j < SET_SIZE_SMALL; j++) {
+                    map.put(Integer.toString(j), j);
+                }
+                map.forEach(consumer);
+            }
+        }
+    }
+
+    @Test
+    public void testForEach_Large() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        BiConsumer<String, Integer> consumer = (s, i) -> {
+        };
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                ArrayMap<String, Integer> map = new ArrayMap<>();
+                for (int j = 0; j < SET_SIZE_LARGE; j++) {
+                    map.put(Integer.toString(j), j);
+                }
+                map.forEach(consumer);
+            }
+        }
+    }
+}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index 4053bdd..8c8d2bf 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -285,7 +285,7 @@
     /** @hide */
     public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
     /** @hide */
-    public static final double DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING = 0.01;
+    public static final float DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING = 0.01f;
     /** @hide */
     public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX = 500;
     /** @hide */
@@ -372,7 +372,7 @@
     /** @hide */
     public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
     /** @hide */
-    public static final double DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING = 0.5;
+    public static final float DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING = 0.5f;
     /** @hide */
     public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX = 15000;
     /** @hide */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index f0745f3..26237c4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -40,6 +40,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -50,6 +51,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
@@ -66,6 +68,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -106,6 +109,7 @@
 import com.android.server.job.controllers.RestrictingController;
 import com.android.server.job.controllers.StateController;
 import com.android.server.job.controllers.StorageController;
+import com.android.server.job.controllers.TareController;
 import com.android.server.job.controllers.TimeController;
 import com.android.server.job.restrictions.JobRestriction;
 import com.android.server.job.restrictions.ThermalStatusRestriction;
@@ -250,6 +254,8 @@
     private final DeviceIdleJobsController mDeviceIdleJobsController;
     /** Needed to get remaining quota time. */
     private final QuotaController mQuotaController;
+    /** Needed to get max execution time and expedited-job allowance. */
+    private final TareController mTareController;
     /**
      * List of restrictions.
      * Note: do not add to or remove from this list at runtime except in the constructor, because we
@@ -344,15 +350,41 @@
     // (ScheduledJobStateChanged and JobStatusDumpProto).
     public static final int RESTRICTED_INDEX = 5;
 
-    private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener {
+    private class ConstantsObserver extends ContentObserver
+            implements DeviceConfig.OnPropertiesChangedListener {
+        private final ContentResolver mContentResolver;
+
+        ConstantsObserver(Handler handler, Context context) {
+            super(handler);
+            mContentResolver = context.getContentResolver();
+        }
+
         public void start() {
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     JobSchedulerBackgroundThread.getExecutor(), this);
+            mContentResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
             // Load all the constants.
+            synchronized (mLock) {
+                mConstants.updateSettingsConstantsLocked(mContentResolver);
+            }
             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
         }
 
         @Override
+        public void onChange(boolean selfChange) {
+            synchronized (mLock) {
+                if (mConstants.updateSettingsConstantsLocked(mContentResolver)) {
+                    for (int controller = 0; controller < mControllers.size(); controller++) {
+                        final StateController sc = mControllers.get(controller);
+                        sc.onConstantsUpdatedLocked();
+                    }
+                    onControllerStateChanged(null);
+                }
+            }
+        }
+
+        @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
             boolean apiQuotaScheduleUpdated = false;
             boolean concurrencyUpdated = false;
@@ -482,6 +514,7 @@
         public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
         @VisibleForTesting
         public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
+        private static final boolean DEFAULT_USE_TARE_POLICY = false;
 
         /**
          * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -559,6 +592,11 @@
          */
         public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
 
+        /**
+         * If true, use TARE policy for job limiting. If false, use quotas.
+         */
+        public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
+
         private void updateBatchingConstantsLocked() {
             MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -637,6 +675,17 @@
                             DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
         }
 
+        private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) {
+            boolean changed = false;
+            final boolean isTareEnabled = Settings.Global.getInt(contentResolver,
+                    Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+            if (USE_TARE_POLICY != isTareEnabled) {
+                USE_TARE_POLICY = isTareEnabled;
+                changed = true;
+            }
+            return changed;
+        }
+
         void dump(IndentingPrintWriter pw) {
             pw.println("Settings:");
             pw.increaseIndent();
@@ -665,6 +714,8 @@
             pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
                     .println();
 
+            pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println();
+
             pw.decreaseIndent();
         }
 
@@ -1130,9 +1181,12 @@
             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
 
             // Return failure early if expedited job quota used up.
-            if (jobStatus.isRequestedExpeditedJob()
-                    && !mQuotaController.isWithinEJQuotaLocked(jobStatus)) {
-                return JobScheduler.RESULT_FAILURE;
+            if (jobStatus.isRequestedExpeditedJob()) {
+                if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus))
+                        || (!mConstants.USE_TARE_POLICY
+                        && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) {
+                    return JobScheduler.RESULT_FAILURE;
+                }
             }
 
             // Give exemption if the source is in the foreground just now.
@@ -1474,7 +1528,7 @@
 
         mHandler = new JobHandler(context.getMainLooper());
         mConstants = new Constants();
-        mConstantsObserver = new ConstantsObserver();
+        mConstantsObserver = new ConstantsObserver(mHandler, context);
         mJobSchedulerStub = new JobSchedulerStub();
 
         mConcurrencyManager = new JobConcurrencyManager(this);
@@ -1519,6 +1573,9 @@
                 new QuotaController(this, backgroundJobsController, connectivityController);
         mControllers.add(mQuotaController);
         mControllers.add(new ComponentController(this));
+        mTareController =
+                new TareController(this, backgroundJobsController, connectivityController);
+        mControllers.add(mTareController);
 
         mRestrictiveControllers = new ArrayList<>();
         mRestrictiveControllers.add(mBatteryController);
@@ -2560,7 +2617,9 @@
     public long getMaxJobExecutionTimeMs(JobStatus job) {
         synchronized (mLock) {
             return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
-                    mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+                    mConstants.USE_TARE_POLICY
+                            ? mTareController.getMaxJobExecutionTimeMsLocked(job)
+                            : mQuotaController.getMaxJobExecutionTimeMsLocked(job));
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index c61a414..5bdee5e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -54,6 +54,9 @@
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
+import com.android.server.tare.EconomicPolicy;
+import com.android.server.tare.EconomyManagerInternal;
+import com.android.server.tare.JobSchedulerEconomicPolicy;
 
 /**
  * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
@@ -107,6 +110,7 @@
     private final Context mContext;
     private final Object mLock;
     private final IBatteryStats mBatteryStats;
+    private final EconomyManagerInternal mEconomyManagerInternal;
     private final JobPackageTracker mJobPackageTracker;
     private final PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
@@ -211,6 +215,7 @@
         mLock = service.getLock();
         mService = service;
         mBatteryStats = batteryStats;
+        mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
         mJobPackageTracker = tracker;
         mCallbackHandler = new JobServiceHandler(looper);
         mJobConcurrencyManager = concurrencyManager;
@@ -288,6 +293,11 @@
             mWakeLock.setReferenceCounted(false);
             mWakeLock.acquire();
 
+            // Note the start when we try to bind so that the app is charged for some processing
+            // even if binding fails.
+            mEconomyManagerInternal.noteInstantaneousEvent(
+                    job.getSourceUserId(), job.getSourcePackageName(),
+                    getStartActionId(job), String.valueOf(job.getJobId()));
             mVerb = VERB_BINDING;
             scheduleOpTimeOutLocked();
             final Intent intent = new Intent().setComponent(job.getServiceComponent());
@@ -350,6 +360,9 @@
             } catch (RemoteException e) {
                 // Whatever.
             }
+            mEconomyManagerInternal.noteOngoingEventStarted(
+                    job.getSourceUserId(), job.getSourcePackageName(),
+                    getRunningActionId(job), String.valueOf(job.getJobId()));
             final String jobPackage = job.getSourcePackageName();
             final int jobUserId = job.getSourceUserId();
             UsageStatsManagerInternal usageStats =
@@ -363,6 +376,22 @@
         }
     }
 
+    @EconomicPolicy.AppAction
+    private static int getStartActionId(@NonNull JobStatus job) {
+        if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
+            return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START;
+        }
+        return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START;
+    }
+
+    @EconomicPolicy.AppAction
+    private static int getRunningActionId(@NonNull JobStatus job) {
+        if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
+            return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
+        }
+        return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
+    }
+
     /**
      * Used externally to query the running job. Will return null if there is no job running.
      */
@@ -982,6 +1011,15 @@
         } catch (RemoteException e) {
             // Whatever.
         }
+        mEconomyManagerInternal.noteOngoingEventStopped(
+                mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
+                getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId()));
+        if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) {
+            mEconomyManagerInternal.noteInstantaneousEvent(
+                    mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
+                    JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
+                    String.valueOf(mRunningJob.getJobId()));
+        }
         if (mWakeLock != null) {
             mWakeLock.release();
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 321c0b3..607ed3c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -661,7 +661,8 @@
             NetworkCapabilities capabilities, Constants constants) {
         // A restricted job that's out of quota MUST use an unmetered network.
         if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
-                && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+                && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)
+                || !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_TARE_WEALTH))) {
             final NetworkCapabilities.Builder builder =
                     copyCapabilities(jobStatus.getJob().getRequiredNetwork());
             builder.addCapability(NET_CAPABILITY_NOT_METERED);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 844a480..3801885 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1141,7 +1141,7 @@
      * treated as an expedited job.
      */
     public boolean shouldTreatAsExpeditedJob() {
-        return mExpeditedQuotaApproved && isRequestedExpeditedJob();
+        return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob();
     }
 
     /**
@@ -1564,7 +1564,8 @@
         // sessions (exempt from dynamic restrictions), we need the additional check to ensure
         // that NEVER jobs don't run.
         // TODO: cleanup quota and standby bucket management so we don't need the additional checks
-        if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
+        if (((!mReadyWithinQuota || !mReadyTareWealth)
+                && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
                 || getEffectiveStandbyBucket() == NEVER_INDEX) {
             return false;
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 36afac8..4ea46eb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -372,6 +372,9 @@
     private final BackgroundJobsController mBackgroundJobsController;
     private final ConnectivityController mConnectivityController;
 
+    @GuardedBy("mLock")
+    private boolean mIsEnabled;
+
     /** How much time each app will have to run jobs within their standby bucket window. */
     private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
 
@@ -590,6 +593,7 @@
         mQcConstants = new QcConstants();
         mBackgroundJobsController = backgroundJobsController;
         mConnectivityController = connectivityController;
+        mIsEnabled = !mConstants.USE_TARE_POLICY;
 
         // Set up the app standby bucketing tracker
         AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
@@ -837,6 +841,9 @@
     /** @return true if the job is within expedited job quota. */
     @GuardedBy("mLock")
     public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) {
+        if (!mIsEnabled) {
+            return true;
+        }
         if (isQuotaFreeLocked(jobStatus.getEffectiveStandbyBucket())) {
             return true;
         }
@@ -884,6 +891,9 @@
 
     @VisibleForTesting
     boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
+        if (!mIsEnabled) {
+            return true;
+        }
         final int standbyBucket = jobStatus.getEffectiveStandbyBucket();
         // A job is within quota if one of the following is true:
         //   1. it was started while the app was in the TOP state
@@ -910,6 +920,9 @@
     @GuardedBy("mLock")
     boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
+        if (!mIsEnabled) {
+            return true;
+        }
         if (standbyBucket == NEVER_INDEX) return false;
 
         if (isQuotaFreeLocked(standbyBucket)) return true;
@@ -2980,7 +2993,8 @@
 
     @Override
     public void onConstantsUpdatedLocked() {
-        if (mQcConstants.mShouldReevaluateConstraints) {
+        if (mQcConstants.mShouldReevaluateConstraints || mIsEnabled == mConstants.USE_TARE_POLICY) {
+            mIsEnabled = !mConstants.USE_TARE_POLICY;
             // Update job bookkeeping out of band.
             JobSchedulerBackgroundThread.getHandler().post(() -> {
                 synchronized (mLock) {
@@ -4120,6 +4134,7 @@
     @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
+        pw.println("Is enabled: " + mIsEnabled);
         pw.println("Is charging: " + mChargeTracker.isChargingLocked());
         pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
         pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index 401646d..be3a3ee 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -27,6 +27,7 @@
 import android.util.SparseArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.tare.EconomyManagerInternal;
@@ -170,6 +171,7 @@
         mBackgroundJobsController = backgroundJobsController;
         mConnectivityController = connectivityController;
         mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
+        mIsEnabled = mConstants.USE_TARE_POLICY;
     }
 
     @Override
@@ -237,6 +239,32 @@
         }
     }
 
+    @Override
+    @GuardedBy("mLock")
+    public void onConstantsUpdatedLocked() {
+        if (mIsEnabled != mConstants.USE_TARE_POLICY) {
+            mIsEnabled = mConstants.USE_TARE_POLICY;
+            // Update job bookkeeping out of band.
+            JobSchedulerBackgroundThread.getHandler().post(() -> {
+                synchronized (mLock) {
+                    final long nowElapsed = sElapsedRealtimeClock.millis();
+                    mService.getJobStore().forEachJob((jobStatus) -> {
+                        if (!mIsEnabled) {
+                            jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true);
+                            setExpeditedTareApproved(jobStatus, nowElapsed, true);
+                        } else {
+                            jobStatus.setTareWealthConstraintSatisfied(
+                                    nowElapsed, hasEnoughWealthLocked(jobStatus));
+                            setExpeditedTareApproved(jobStatus, nowElapsed,
+                                    jobStatus.isRequestedExpeditedJob()
+                                            && canAffordExpeditedBillLocked(jobStatus));
+                        }
+                    });
+                }
+            });
+        }
+    }
+
     @GuardedBy("mLock")
     public boolean canScheduleEJ(@NonNull JobStatus jobStatus) {
         if (!mIsEnabled) {
@@ -246,6 +274,21 @@
     }
 
     @GuardedBy("mLock")
+    public long getMaxJobExecutionTimeMsLocked(@NonNull JobStatus jobStatus) {
+        if (!mIsEnabled) {
+            return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
+        }
+        if (jobStatus.shouldTreatAsExpeditedJob()) {
+            return mEconomyManagerInternal.getMaxDurationMs(
+                    jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+                    BILL_JOB_RUNNING_EXPEDITED);
+        }
+        return mEconomyManagerInternal.getMaxDurationMs(
+                jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+                BILL_JOB_RUNNING_DEFAULT);
+    }
+
+    @GuardedBy("mLock")
     private void addJobToBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
         final int userId = jobStatus.getSourceUserId();
         final String pkgName = jobStatus.getSourcePackageName();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 829ad27..d7c3a86 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -238,8 +238,9 @@
     }
 
     @GuardedBy("mLock")
-    void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
-            @Nullable String tag, final long startElapsed, final boolean updateBalanceCheck) {
+    private void noteOngoingEventLocked(final int userId, @NonNull final String pkgName,
+            final int eventId, @Nullable String tag, final long startElapsed,
+            final boolean updateBalanceCheck) {
         SparseArrayMap<String, OngoingEvent> ongoingEvents =
                 mCurrentOngoingEvents.get(userId, pkgName);
         if (ongoingEvents == null) {
@@ -398,8 +399,8 @@
      *                                    registered bills and notify listeners about any changes.
      */
     @GuardedBy("mLock")
-    void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
-            @Nullable String tag, final long nowElapsed, final long now,
+    private void stopOngoingActionLocked(final int userId, @NonNull final String pkgName,
+            final int eventId, @Nullable String tag, final long nowElapsed, final long now,
             final boolean updateBalanceCheck, final boolean notifyOnAffordabilityChange) {
         final Ledger ledger = getLedgerLocked(userId, pkgName);
 
@@ -408,7 +409,7 @@
         if (ongoingEvents == null) {
             // This may occur if TARE goes from disabled to enabled while an event is already
             // occurring.
-            Slog.w(TAG, "No ongoing transactions for <" + userId + ">" + pkgName);
+            Slog.w(TAG, "No ongoing transactions for " + appToString(userId, pkgName));
             return;
         }
         final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
@@ -417,7 +418,7 @@
             // occurring.
             Slog.w(TAG, "Nonexistent ongoing transaction "
                     + eventToString(eventId) + (tag == null ? "" : ":" + tag)
-                    + " for <" + userId + ">" + pkgName + " ended");
+                    + " for " + appToString(userId, pkgName) + " ended");
             return;
         }
         ongoingEvent.refCount--;
@@ -472,7 +473,8 @@
             Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
                     + eventToString(transaction.eventId)
                     + (transaction.tag == null ? "" : ":" + transaction.tag)
-                    + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+                    + " for " + appToString(userId, pkgName)
+                    + " by " + (transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
                     transaction.eventId, transaction.tag, newDelta);
@@ -485,7 +487,8 @@
             Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
                     + eventToString(transaction.eventId)
                     + (transaction.tag == null ? "" : ":" + transaction.tag)
-                    + " for <" + userId + ">" + pkgName + " by " + (transaction.delta - newDelta));
+                    + " for " + appToString(userId, pkgName)
+                    + " by " + (transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
                     transaction.eventId, transaction.tag, newDelta);
@@ -548,7 +551,7 @@
                 }
                 if (toReclaim > 0) {
                     Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
-                            + " from <" + userId + ">" + pkgName);
+                            + " from " + appToString(userId, pkgName));
 
                     recordTransactionLocked(userId, pkgName, ledger,
                             new Ledger.Transaction(
@@ -845,7 +848,7 @@
 
             @Override
             public String toString() {
-                return "<" + userId + ">" + packageName;
+                return appToString(userId, packageName);
             }
 
             @Override
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index acdf689..6ef9456 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -16,6 +16,79 @@
 
 package com.android.server.tare;
 
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
+
 import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
 import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
@@ -24,6 +97,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
 import android.util.SparseArray;
 
 /**
@@ -31,6 +111,8 @@
  * AlarmManager.
  */
 public class AlarmManagerEconomicPolicy extends EconomicPolicy {
+    private static final String TAG = "TARE- " + AlarmManagerEconomicPolicy.class.getSimpleName();
+
     public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
             TYPE_ACTION | POLICY_AM | 0;
     public static final int ACTION_ALARM_WAKEUP_EXACT =
@@ -57,30 +139,50 @@
             COST_MODIFIER_PROCESS_STATE
     };
 
+    private long mMinSatiatedBalance;
+    private long mMaxSatiatedBalance;
+    private long mMaxSatiatedCirculation;
+
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+    private final SettingsObserver mSettingsObserver;
+    private final InternalResourceService mInternalResourceService;
+
     private final SparseArray<Action> mActions = new SparseArray<>();
     private final SparseArray<Reward> mRewards = new SparseArray<>();
 
     AlarmManagerEconomicPolicy(InternalResourceService irs) {
         super(irs);
-        loadActions();
-        loadRewards();
+        mInternalResourceService = irs;
+        mSettingsObserver = new SettingsObserver(TareHandlerThread.getHandler());
+        loadConstants("");
+    }
+
+    @Override
+    void setup() {
+        super.setup();
+        ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        loadConstants(Settings.Global.getString(
+                mInternalResourceService.getContext().getContentResolver(),
+                TARE_ALARM_MANAGER_CONSTANTS));
     }
 
     @Override
     long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
         // TODO: take exemption into account
-        return arcToNarc(160);
+        return mMinSatiatedBalance;
     }
 
     @Override
     long getMaxSatiatedBalance() {
-        return arcToNarc(1440);
+        return mMaxSatiatedBalance;
     }
 
-
     @Override
     long getMaxSatiatedCirculation() {
-        return arcToNarc(52000);
+        return mMaxSatiatedCirculation;
     }
 
     @NonNull
@@ -101,43 +203,162 @@
         return mRewards.get(rewardId);
     }
 
-    private void loadActions() {
+    private void loadConstants(String policyValuesString) {
+        mActions.clear();
+        mRewards.clear();
+
+        try {
+            mParser.setString(policyValuesString);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Global setting key incorrect: ", e);
+        }
+
+        mMinSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
+                        DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
+        mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
+                DEFAULT_AM_MAX_SATIATED_BALANCE));
+        mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_AM_MAX_CIRCULATION,
+                DEFAULT_AM_MAX_CIRCULATION));
+
+        final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
+                mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
+                        DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE));
+
         mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
-                new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, arcToNarc(3), arcToNarc(5)));
+                new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP)),
+                        exactAllowWhileIdleWakeupBasePrice));
         mActions.put(ACTION_ALARM_WAKEUP_EXACT,
-                new Action(ACTION_ALARM_WAKEUP_EXACT, arcToNarc(3), arcToNarc(4)));
+                new Action(ACTION_ALARM_WAKEUP_EXACT,
+                        arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP)),
+                        arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
+                                DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE))));
+
+        final long inexactAllowWhileIdleWakeupBasePrice =
+                arcToNarc(mParser.getInt(
+                        KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
+                        DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE));
+
         mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
-                        arcToNarc(3), arcToNarc(4)));
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP)),
+                        inexactAllowWhileIdleWakeupBasePrice));
         mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
-                new Action(ACTION_ALARM_WAKEUP_INEXACT, arcToNarc(3), arcToNarc(3)));
+                new Action(ACTION_ALARM_WAKEUP_INEXACT,
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
+                                DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE))));
+
+        final long exactAllowWhileIdleNonWakeupBasePrice =
+                arcToNarc(mParser.getInt(
+                        KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
+                        DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
+
         mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
-                        arcToNarc(1), arcToNarc(3)));
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP)),
+                        exactAllowWhileIdleNonWakeupBasePrice));
         mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
-                new Action(ACTION_ALARM_NONWAKEUP_EXACT, arcToNarc(1), arcToNarc(2)));
+                new Action(ACTION_ALARM_NONWAKEUP_EXACT,
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
+                                DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE))));
+
+        final long inexactAllowWhileIdleNonWakeupBasePrice =
+                arcToNarc(mParser.getInt(
+                        KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
+                        DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
+
         mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                 new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
-                        arcToNarc(1), arcToNarc(2)));
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP)),
+                        inexactAllowWhileIdleNonWakeupBasePrice));
         mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
-                new Action(ACTION_ALARM_NONWAKEUP_INEXACT, arcToNarc(1), arcToNarc(1)));
+                new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
+                                DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
+                                DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE))));
         mActions.put(ACTION_ALARM_CLOCK,
-                new Action(ACTION_ALARM_CLOCK, arcToNarc(5), arcToNarc(10)));
-    }
+                new Action(ACTION_ALARM_CLOCK,
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
+                                DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
+                                DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE))));
 
-    private void loadRewards() {
-        mRewards.put(REWARD_TOP_ACTIVITY,
-                new Reward(REWARD_TOP_ACTIVITY,
-                        arcToNarc(0), /* .01 arcs */ arcToNarc(1) / 100, arcToNarc(500)));
-        mRewards.put(REWARD_NOTIFICATION_SEEN,
-                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(3), arcToNarc(0), arcToNarc(60)));
+        mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
+                        DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT)),
+                (long) (arcToNarc(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
+                        DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX,
+                                DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX))));
+        mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
+                        DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT)),
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
+                        DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
+                        DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX))));
         mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                 new Reward(REWARD_NOTIFICATION_INTERACTION,
-                        arcToNarc(5), arcToNarc(0), arcToNarc(500)));
-        mRewards.put(REWARD_WIDGET_INTERACTION,
-                new Reward(REWARD_WIDGET_INTERACTION, arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
+                                DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
+                                DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
+                                DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX))));
+        mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
+                        DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT)),
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
+                        DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
+                        DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX))));
         mRewards.put(REWARD_OTHER_USER_INTERACTION,
                 new Reward(REWARD_OTHER_USER_INTERACTION,
-                        arcToNarc(10), arcToNarc(0), arcToNarc(500)));
+                        arcToNarc(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
+                                DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
+                                DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING)),
+                        arcToNarc(mParser.getInt(
+                                KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
+                                DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX))));
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            loadConstants(Settings.Global.getString(
+                    mInternalResourceService.getContext().getContentResolver(),
+                    TARE_ALARM_MANAGER_CONSTANTS));
+        }
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index 8c56c61..f05e5c9 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -67,6 +67,14 @@
     }
 
     @Override
+    void setup() {
+        super.setup();
+        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+            mEnabledEconomicPolicies.valueAt(i).setup();
+        }
+    }
+
+    @Override
     public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
         long min = 0;
         for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index a2ba75c..b435c9e 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -19,12 +19,15 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
+import static com.android.server.tare.TareUtils.appToString;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.tare.IEconomyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -161,6 +164,18 @@
         }
     };
 
+    private final UsageStatsManagerInternal.UsageEventListener mSurveillanceAgent =
+            new UsageStatsManagerInternal.UsageEventListener() {
+                /**
+                 * Callback to inform listeners of a new event.
+                 */
+                @Override
+                public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
+                    mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event)
+                            .sendToTarget();
+                }
+            };
+
     private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener =
             new AlarmManager.OnAlarmListener() {
                 @Override
@@ -175,6 +190,7 @@
 
     private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
     private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
+    private static final int MSG_PROCESS_USAGE_EVENT = 2;
     private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
     private static final String KEY_PKG = "pkg";
 
@@ -374,6 +390,42 @@
     }
 
     @GuardedBy("mLock")
+    private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
+        if (!mIsEnabled) {
+            return;
+        }
+        final String pkgName = event.getPackageName();
+        if (DEBUG) {
+            Slog.d(TAG, "Processing event " + event.getEventType()
+                    + " for " + appToString(userId, pkgName));
+        }
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        switch (event.getEventType()) {
+            case UsageEvents.Event.ACTIVITY_RESUMED:
+                mAgent.noteOngoingEventLocked(userId, pkgName,
+                        EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed);
+                break;
+            case UsageEvents.Event.ACTIVITY_PAUSED:
+            case UsageEvents.Event.ACTIVITY_STOPPED:
+            case UsageEvents.Event.ACTIVITY_DESTROYED:
+                final long now = getCurrentTimeMillis();
+                mAgent.stopOngoingActionLocked(userId, pkgName,
+                        EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed, now);
+                break;
+            case UsageEvents.Event.USER_INTERACTION:
+            case UsageEvents.Event.CHOOSER_ACTION:
+                mAgent.noteInstantaneousEventLocked(userId, pkgName,
+                        EconomicPolicy.REWARD_OTHER_USER_INTERACTION, null);
+                break;
+            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+            case UsageEvents.Event.NOTIFICATION_SEEN:
+                mAgent.noteInstantaneousEventLocked(userId, pkgName,
+                        EconomicPolicy.REWARD_NOTIFICATION_SEEN, null);
+                break;
+        }
+    }
+
+    @GuardedBy("mLock")
     private void scheduleUnusedWealthReclamationLocked() {
         final long now = getCurrentTimeMillis();
         final long nextReclamationTime =
@@ -418,7 +470,7 @@
         mPkgCache = mPackageManager.getInstalledPackages(0);
     }
 
-    private void registerReceivers() {
+    private void registerListeners() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
@@ -435,6 +487,9 @@
         userFilter.addAction(Intent.ACTION_USER_ADDED);
         getContext()
                 .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+        UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class);
+        usmi.registerListener(mSurveillanceAgent);
     }
 
     /** Perform long-running and/or heavy setup work. This should be called off the main thread. */
@@ -454,7 +509,7 @@
             return;
         }
         synchronized (mLock) {
-            registerReceivers();
+            registerListeners();
             mCurrentBatteryLevel = getCurrentBatteryLevel();
             mHandler.post(this::setupHeavyWork);
             scheduleUnusedWealthReclamationLocked();
@@ -479,6 +534,9 @@
             mPkgCache.clear();
             mUidToPackageCache.clear();
             getContext().unregisterReceiver(mBroadcastReceiver);
+            UsageStatsManagerInternal usmi =
+                    LocalServices.getService(UsageStatsManagerInternal.class);
+            usmi.unregisterListener(mSurveillanceAgent);
         }
         synchronized (mPackageToUidCache) {
             mPackageToUidCache.clear();
@@ -507,6 +565,15 @@
                 }
                 break;
 
+                case MSG_PROCESS_USAGE_EVENT: {
+                    final int userId = msg.arg1;
+                    final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
+                    synchronized (mLock) {
+                        processUsageEventLocked(userId, event);
+                    }
+                }
+                break;
+
                 case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: {
                     removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT);
                     synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index ff008a2..e339650 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -16,6 +16,88 @@
 
 package com.android.server.tare;
 
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+
 import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
 import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
@@ -24,6 +106,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
 import android.util.SparseArray;
 
 /**
@@ -31,6 +120,8 @@
  * JobScheduler.
  */
 public class JobSchedulerEconomicPolicy extends EconomicPolicy {
+    private static final String TAG = "TARE- " + JobSchedulerEconomicPolicy.class.getSimpleName();
+
     public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JS | 0;
     public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JS | 1;
     public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JS | 2;
@@ -50,29 +141,50 @@
             COST_MODIFIER_PROCESS_STATE
     };
 
+    private long mMinSatiatedBalance;
+    private long mMaxSatiatedBalance;
+    private long mMaxSatiatedCirculation;
+
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+    private final SettingsObserver mSettingsObserver;
+    private final InternalResourceService mInternalResourceService;
+
     private final SparseArray<Action> mActions = new SparseArray<>();
     private final SparseArray<Reward> mRewards = new SparseArray<>();
 
     JobSchedulerEconomicPolicy(InternalResourceService irs) {
         super(irs);
-        loadActions();
-        loadRewards();
+        mInternalResourceService = irs;
+        mSettingsObserver = new SettingsObserver(TareHandlerThread.getHandler());
+        loadConstants("");
+    }
+
+    @Override
+    void setup() {
+        super.setup();
+        ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS),
+                false, mSettingsObserver, UserHandle.USER_ALL);
+        loadConstants(Settings.Global.getString(
+                mInternalResourceService.getContext().getContentResolver(),
+                TARE_JOB_SCHEDULER_CONSTANTS));
     }
 
     @Override
     long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
         // TODO: incorporate time since usage
-        return arcToNarc(2000);
+        return mMinSatiatedBalance;
     }
 
     @Override
     long getMaxSatiatedBalance() {
-        return arcToNarc(60000);
+        return mMaxSatiatedBalance;
     }
 
     @Override
     long getMaxSatiatedCirculation() {
-        return arcToNarc(691200);
+        return mMaxSatiatedCirculation;
     }
 
     @NonNull
@@ -93,45 +205,134 @@
         return mRewards.get(rewardId);
     }
 
-    private void loadActions() {
-        mActions.put(ACTION_JOB_MAX_START,
-                new Action(ACTION_JOB_MAX_START, arcToNarc(3), arcToNarc(10)));
-        mActions.put(ACTION_JOB_MAX_RUNNING,
-                new Action(ACTION_JOB_MAX_RUNNING, arcToNarc(2), arcToNarc(5)));
-        mActions.put(ACTION_JOB_HIGH_START,
-                new Action(ACTION_JOB_HIGH_START, arcToNarc(3), arcToNarc(8)));
-        mActions.put(ACTION_JOB_HIGH_RUNNING,
-                new Action(ACTION_JOB_HIGH_RUNNING, arcToNarc(2), arcToNarc(4)));
-        mActions.put(ACTION_JOB_DEFAULT_START,
-                new Action(ACTION_JOB_DEFAULT_START, arcToNarc(3), arcToNarc(6)));
-        mActions.put(ACTION_JOB_DEFAULT_RUNNING,
-                new Action(ACTION_JOB_DEFAULT_RUNNING, arcToNarc(2), arcToNarc(3)));
-        mActions.put(ACTION_JOB_LOW_START,
-                new Action(ACTION_JOB_LOW_START, arcToNarc(3), arcToNarc(4)));
-        mActions.put(ACTION_JOB_LOW_RUNNING,
-                new Action(ACTION_JOB_LOW_RUNNING, arcToNarc(2), arcToNarc(2)));
-        mActions.put(ACTION_JOB_MIN_START,
-                new Action(ACTION_JOB_MIN_START, arcToNarc(3), arcToNarc(2)));
-        mActions.put(ACTION_JOB_MIN_RUNNING,
-                new Action(ACTION_JOB_MIN_RUNNING, arcToNarc(2), arcToNarc(1)));
-        mActions.put(ACTION_JOB_TIMEOUT,
-                new Action(ACTION_JOB_TIMEOUT, arcToNarc(30), arcToNarc(60)));
-    }
+    private void loadConstants(String policyValuesString) {
+        mActions.clear();
+        mRewards.clear();
 
-    private void loadRewards() {
-        mRewards.put(REWARD_TOP_ACTIVITY,
-                new Reward(REWARD_TOP_ACTIVITY,
-                        arcToNarc(0), /* .5 arcs */ arcToNarc(5) / 10, arcToNarc(15000)));
-        mRewards.put(REWARD_NOTIFICATION_SEEN,
-                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(1), arcToNarc(0), arcToNarc(10)));
+        try {
+            mParser.setString(policyValuesString);
+        } catch (IllegalArgumentException e) {
+            Slog.e(TAG, "Global setting key incorrect: ", e);
+        }
+
+        mMinSatiatedBalance = arcToNarc(
+                mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
+                        DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
+        mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
+                DEFAULT_JS_MAX_SATIATED_BALANCE));
+        mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_JS_MAX_CIRCULATION,
+                DEFAULT_JS_MAX_CIRCULATION));
+
+        mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
+                        DEFAULT_JS_ACTION_JOB_MAX_START_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE))));
+        mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
+                        DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE))));
+        mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_CTP,
+                        DEFAULT_JS_ACTION_JOB_HIGH_START_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE))));
+        mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
+                        DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE))));
+        mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
+                        DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE))));
+        mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
+                        DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE))));
+        mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_CTP,
+                        DEFAULT_JS_ACTION_JOB_LOW_START_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE))));
+        mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
+                        DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE))));
+        mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_CTP,
+                        DEFAULT_JS_ACTION_JOB_MIN_START_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE))));
+        mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
+                        DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE))));
+        mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
+                        DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP)),
+                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
+                        DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE))));
+
+        mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
+                        DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT)),
+                (long) (arcToNarc(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
+                        DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX,
+                        DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX))));
+        mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
+                        DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT)),
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
+                        DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
+                        DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX))));
         mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                 new Reward(REWARD_NOTIFICATION_INTERACTION,
-                        arcToNarc(5), arcToNarc(0), arcToNarc(5000)));
-        mRewards.put(REWARD_WIDGET_INTERACTION,
-                new Reward(REWARD_WIDGET_INTERACTION,
-                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+                        arcToNarc(mParser.getInt(
+                                KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
+                                DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
+                        arcToNarc(mParser.getInt(
+                                KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
+                                DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
+                        arcToNarc(mParser.getInt(
+                                KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
+                                DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX))));
+        mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
+                        DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT)),
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
+                        DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING)),
+                arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
+                        DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX))));
         mRewards.put(REWARD_OTHER_USER_INTERACTION,
                 new Reward(REWARD_OTHER_USER_INTERACTION,
-                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
+                        arcToNarc(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
+                                DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT)),
+                        arcToNarc(mParser.getInt(
+                                KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
+                                DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING)),
+                        arcToNarc(mParser.getInt(
+                                KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
+                                DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX))));
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            loadConstants(Settings.Global.getString(
+                    mInternalResourceService.getContext().getContentResolver(),
+                    TARE_JOB_SCHEDULER_CONSTANTS));
+        }
     }
 }
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 1bf732b..d963e68 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -120,7 +120,7 @@
     srcs: [
         "java/android/media/ApplicationMediaCapabilities.java",
         "java/android/media/MediaFeature.java",
-        "java/android/media/MediaTranscodeManager.java",
+        "java/android/media/MediaTranscodingManager.java",
     ],
     path: "java",
 }
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ce68447..6eea769 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -1,15 +1,15 @@
 // Signature format: 2.0
 package android.media {
 
-  public final class MediaTranscodeManager {
-    method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener);
+  public final class MediaTranscodingManager {
+    method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener);
   }
 
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
-    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
+  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener {
+    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession);
   }
 
-  public abstract static class MediaTranscodeManager.TranscodingRequest {
+  public abstract static class MediaTranscodingManager.TranscodingRequest {
     method public int getClientPid();
     method public int getClientUid();
     method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
@@ -18,13 +18,13 @@
     method @NonNull public android.net.Uri getSourceUri();
   }
 
-  public static class MediaTranscodeManager.TranscodingRequest.VideoFormatResolver {
-    ctor public MediaTranscodeManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
+  public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver {
+    ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
     method @Nullable public android.media.MediaFormat resolveVideoFormat();
     method public boolean shouldTranscode();
   }
 
-  public static final class MediaTranscodeManager.TranscodingSession {
+  public static final class MediaTranscodingManager.TranscodingSession {
     method public boolean addClientUid(int);
     method public void cancel();
     method @NonNull public java.util.List<java.lang.Integer> getClientUids();
@@ -33,7 +33,7 @@
     method public int getResult();
     method public int getSessionId();
     method public int getStatus();
-    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener);
     field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
     field public static final int ERROR_NONE = 0; // 0x0
     field public static final int ERROR_SERVICE_DIED = 2; // 0x2
@@ -47,21 +47,21 @@
     field public static final int STATUS_RUNNING = 2; // 0x2
   }
 
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener {
-    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
+  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener {
+    method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int);
   }
 
-  public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest {
+  public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest {
     method @NonNull public android.media.MediaFormat getVideoTrackFormat();
   }
 
-  public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder {
-    ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+  public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder {
+    ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
   }
 
 }
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index de2924e..75a56b72 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -75,8 +75,8 @@
     public static void registerServiceWrappers() {
         SystemServiceRegistry.registerContextAwareService(
                 Context.MEDIA_TRANSCODING_SERVICE,
-                MediaTranscodeManager.class,
-                context -> new MediaTranscodeManager(context)
+                MediaTranscodingManager.class,
+                context -> new MediaTranscodingManager(context)
         );
         if (SdkLevel.isAtLeastS()) {
             SystemServiceRegistry.registerContextAwareService(
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
similarity index 98%
rename from apex/media/framework/java/android/media/MediaTranscodeManager.java
rename to apex/media/framework/java/android/media/MediaTranscodingManager.java
index 5742d43..93d58d0 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java
@@ -54,7 +54,7 @@
 /**
  Android 12 introduces Compatible media transcoding feature.  See
  <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media
+ Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media
  transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
  AVC.
 
@@ -69,7 +69,7 @@
  <p>
  To transcode a media file, first create a {@link TranscodingRequest} through its builder class
  {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
- {@link MediaTranscodeManager#enqueueRequest(
+ {@link MediaTranscodingManager#enqueueRequest(
          TranscodingRequest, Executor, OnTranscodingFinishedListener)}
  TranscodeRequest are processed based on client process's priority and request priority. When a
  transcode operation is completed the caller is notified via its
@@ -87,8 +87,8 @@
  */
 @MinSdk(Build.VERSION_CODES.S)
 @SystemApi
-public final class MediaTranscodeManager {
-    private static final String TAG = "MediaTranscodeManager";
+public final class MediaTranscodingManager {
+    private static final String TAG = "MediaTranscodingManager";
 
     /** Maximum number of retry to connect to the service. */
     private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
@@ -127,7 +127,7 @@
     private final Object mLock = new Object();
     @GuardedBy("mLock")
     @NonNull private ITranscodingClient mTranscodingClient = null;
-    private static MediaTranscodeManager sMediaTranscodeManager;
+    private static MediaTranscodingManager sMediaTranscodingManager;
 
     private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) {
         synchronized (mPendingTranscodingSessions) {
@@ -306,7 +306,7 @@
                 }
 
                 try {
-                    // Do not set hasRetried for retry initiated by MediaTranscodeManager.
+                    // Do not set hasRetried for retry initiated by MediaTranscodingManager.
                     session.retryInternal(false /*setHasRetried*/);
                 } catch (Exception re) {
                     // TODO(hkuang): Return correct error code to the client.
@@ -423,7 +423,7 @@
     /**
      * @hide
      */
-    public MediaTranscodeManager(@NonNull Context context) {
+    public MediaTranscodingManager(@NonNull Context context) {
         mContext = context;
         mContentResolver = mContext.getContentResolver();
         mPackageName = mContext.getPackageName();
@@ -1348,7 +1348,7 @@
                     @IntRange(from = 0, to = 100) int progress);
         }
 
-        private final MediaTranscodeManager mManager;
+        private final MediaTranscodingManager mManager;
         private Executor mListenerExecutor;
         private OnTranscodingFinishedListener mListener;
         private int mSessionId = -1;
@@ -1374,7 +1374,7 @@
         private final TranscodingRequest mRequest;
 
         private TranscodingSession(
-                @NonNull MediaTranscodeManager manager,
+                @NonNull MediaTranscodingManager manager,
                 @NonNull TranscodingRequest request,
                 @NonNull TranscodingSessionParcel parcel,
                 @NonNull @CallbackExecutor Executor executor,
@@ -1675,10 +1675,10 @@
 
     /**
      * Enqueues a TranscodingRequest for execution.
-     * <p> Upon successfully accepting the request, MediaTranscodeManager will return a
+     * <p> Upon successfully accepting the request, MediaTranscodingManager will return a
      * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to
      * track the progress and get the result.
-     * <p> MediaTranscodeManager will return null if fails to accept the request due to service
+     * <p> MediaTranscodingManager will return null if fails to accept the request due to service
      * rebooting. Client could retry again after receiving null.
      *
      * @param transcodingRequest The TranscodingRequest to enqueue.
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 6e27aff..929e63e 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -107,9 +107,13 @@
 static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
 static constexpr size_t TEXT_POS_LEN_MAX = 16;
+static const int DYNAMIC_COLOR_COUNT = 4;
 static const char U_TEXTURE[] = "uTexture";
 static const char U_FADE[] = "uFade";
 static const char U_CROP_AREA[] = "uCropArea";
+static const char U_START_COLOR_PREFIX[] = "uStartColor";
+static const char U_END_COLOR_PREFIX[] = "uEndColor";
+static const char U_COLOR_PROGRESS[] = "uColorProgress";
 static const char A_UV[] = "aUv";
 static const char A_POSITION[] = "aPosition";
 static const char VERTEX_SHADER_SOURCE[] = R"(
@@ -121,6 +125,28 @@
         gl_Position = aPosition;
         vUv = aUv;
     })";
+static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
+    precision mediump float;
+    uniform sampler2D uTexture;
+    uniform float uFade;
+    uniform float uColorProgress;
+    uniform vec4 uStartColor0;
+    uniform vec4 uStartColor1;
+    uniform vec4 uStartColor2;
+    uniform vec4 uStartColor3;
+    uniform vec4 uEndColor0;
+    uniform vec4 uEndColor1;
+    uniform vec4 uEndColor2;
+    uniform vec4 uEndColor3;
+    varying highp vec2 vUv;
+    void main() {
+        vec4 mask = texture2D(uTexture, vUv);
+        vec4 color = mask.r * mix(uStartColor0, uEndColor0, uColorProgress)
+            + mask.g * mix(uStartColor1, uEndColor1, uColorProgress)
+            + mask.b * mix(uStartColor2, uEndColor2, uColorProgress)
+            + mask.a * mix(uStartColor3, uEndColor3, uColorProgress);
+        gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+    })";
 static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
     precision mediump float;
     uniform sampler2D uTexture;
@@ -128,7 +154,7 @@
     varying highp vec2 vUv;
     void main() {
         vec4 color = texture2D(uTexture, vUv);
-        gl_FragColor = vec4(color.x, color.y, color.z, 1.0 - uFade);
+        gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
     })";
 static const char TEXT_FRAG_SHADER_SOURCE[] = R"(
     precision mediump float;
@@ -226,6 +252,10 @@
     outInfo->stride = AImageDecoder_getMinimumStride(decoder);
     outInfo->flags = 0;
 
+    // Set decoding option to alpha unpremultiplied so that the R, G, B channels
+    // of transparent pixels are preserved.
+    AImageDecoder_setUnpremultipliedRequired(decoder, true);
+
     const size_t size = outInfo->stride * outInfo->height;
     void* pixels = malloc(size);
     int result = AImageDecoder_decodeImage(decoder, pixels, outInfo->stride, size);
@@ -675,9 +705,12 @@
 }
 
 void BootAnimation::initShaders() {
+    bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled;
     GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
     GLuint imageFragmentShader =
-        compileShader(GL_FRAGMENT_SHADER, (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
+        compileShader(GL_FRAGMENT_SHADER, dynamicColoringEnabled
+            ? (const GLchar *)IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE
+            : (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
     GLuint textFragmentShader =
         compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE);
 
@@ -692,6 +725,22 @@
     glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
     glEnableVertexAttribArray(uvLocation);
 
+    if (dynamicColoringEnabled) {
+        glUseProgram(mImageShader);
+        SLOGI("[BootAnimation] Dynamically coloring boot animation.");
+        for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+            float *startColor = mAnimation->startColors[i];
+            float *endColor = mAnimation->endColors[i];
+            glUniform4f(glGetUniformLocation(mImageShader,
+                (U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
+                startColor[0], startColor[1], startColor[2], 1 /* alpha */);
+            glUniform4f(glGetUniformLocation(mImageShader,
+                (U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
+                endColor[0], endColor[1], endColor[2], 1 /* alpha */);
+        }
+        mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
+    }
+
     // Initialize text shader.
     mTextShader = linkShader(vertexShader, textFragmentShader);
     positionLocation = glGetAttribLocation(mTextShader, A_POSITION);
@@ -869,6 +918,20 @@
     return true;
 }
 
+// Parse a color represented as a signed decimal int string.
+// E.g. "-2757722" (whose hex 2's complement is 0xFFD5EBA6).
+// If the input color string is empty, set color with values in defaultColor.
+static void parseColorDecimalString(const std::string& colorString,
+    float color[3], float defaultColor[3]) {
+    if (colorString == "") {
+        memcpy(color, defaultColor, sizeof(float) * 3);
+        return;
+    }
+    int colorInt = atoi(colorString.c_str());
+    color[0] = ((float)((colorInt >> 16) & 0xFF)) / 0xFF; // r
+    color[1] = ((float)((colorInt >> 8) & 0xFF)) / 0xFF; // g
+    color[2] = ((float)(colorInt & 0xFF)) / 0xFF; // b
+}
 
 static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
     ZipEntryRO entry = zip->findEntryByName(name);
@@ -1010,6 +1073,8 @@
         return false;
     }
     char const* s = desString.string();
+    std::string dynamicColoringPartName = "";
+    bool postDynamicColoring = false;
 
     // Parse the description file
     for (;;) {
@@ -1028,7 +1093,13 @@
         char color[7] = "000000"; // default to black if unspecified
         char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
         char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
+        char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX];
         char pathType;
+        // start colors default to black if unspecified
+        char start_color_0[7] = "000000";
+        char start_color_1[7] = "000000";
+        char start_color_2[7] = "000000";
+        char start_color_3[7] = "000000";
 
         int nextReadPos;
 
@@ -1043,6 +1114,15 @@
             } else {
               animation.progressEnabled = false;
             }
+        } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s",
+            dynamicColoringPartNameBuffer,
+            start_color_0, start_color_1, start_color_2, start_color_3)) {
+            animation.dynamicColoringEnabled = true;
+            parseColor(start_color_0, animation.startColors[0]);
+            parseColor(start_color_1, animation.startColors[1]);
+            parseColor(start_color_2, animation.startColors[2]);
+            parseColor(start_color_3, animation.startColors[3]);
+            dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
         } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
                           &pathType, &count, &pause, path, &nextReadPos) >= 4) {
             if (pathType == 'f') {
@@ -1055,6 +1135,16 @@
             //       "clockPos1=%s, clockPos2=%s",
             //       pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
             Animation::Part part;
+            if (path == dynamicColoringPartName) {
+                // Part is specified to use dynamic coloring.
+                part.useDynamicColoring = true;
+                part.postDynamicColoring = false;
+                postDynamicColoring = true;
+            } else {
+                // Part does not use dynamic coloring.
+                part.useDynamicColoring = false;
+                part.postDynamicColoring =  postDynamicColoring;
+            }
             part.playUntilComplete = pathType == 'c';
             part.framesToFadeCount = framesToFadeCount;
             part.count = count;
@@ -1086,6 +1176,12 @@
         s = ++endl;
     }
 
+    for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+        parseColorDecimalString(
+            android::base::GetProperty("persist.bootanim.color" + std::to_string(i + 1), ""),
+            animation.endColors[i], animation.startColors[i]);
+    }
+
     return true;
 }
 
@@ -1357,6 +1453,14 @@
             for (size_t j=0 ; j<fcount ; j++) {
                 if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
 
+                // Color progress is
+                // - the normalized animation progress between [0, 1] for the dynamic coloring part,
+                // - 0 for parts that come before,
+                // - 1 for parts that come after.
+                float colorProgress = part.useDynamicColoring
+                    ? (float)j / fcount
+                    : (part.postDynamicColoring ? 1 : 0);
+
                 processDisplayEvents();
 
                 const int animationX = (mWidth - animation.width) / 2;
@@ -1376,19 +1480,7 @@
 
                 const int xc = animationX + frame.trimX;
                 const int yc = animationY + frame.trimY;
-                Region clearReg(Rect(mWidth, mHeight));
-                clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
-                if (!clearReg.isEmpty()) {
-                    Region::const_iterator head(clearReg.begin());
-                    Region::const_iterator tail(clearReg.end());
-                    glEnable(GL_SCISSOR_TEST);
-                    while (head != tail) {
-                        const Rect& r2(*head++);
-                        glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
-                        glClear(GL_COLOR_BUFFER_BIT);
-                    }
-                    glDisable(GL_SCISSOR_TEST);
-                }
+                glClear(GL_COLOR_BUFFER_BIT);
                 // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
                 // which is equivalent to mHeight - (yc + frame.trimHeight)
                 const int frameDrawY = mHeight - (yc + frame.trimHeight);
@@ -1404,6 +1496,9 @@
                 glUseProgram(mImageShader);
                 glUniform1i(mImageTextureLocation, 0);
                 glUniform1f(mImageFadeLocation, fade);
+                if (animation.dynamicColoringEnabled) {
+                    glUniform1f(mImageColorProgressLocation, colorProgress);
+                }
                 glEnable(GL_BLEND);
                 drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight);
                 glDisable(GL_BLEND);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 7b616d9..2c861eb 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -90,6 +90,10 @@
             uint8_t* audioData;
             int audioLength;
             Animation* animation;
+            // Controls if dynamic coloring is enabled for this part.
+            bool useDynamicColoring = false;
+            // Defines if this part is played after the dynamic coloring part.
+            bool postDynamicColoring = false;
 
             bool hasFadingPhase() const {
                 return !playUntilComplete && framesToFadeCount > 0;
@@ -105,6 +109,10 @@
         ZipFileRO* zip;
         Font clockFont;
         Font progressFont;
+         // Controls if dynamic coloring is enabled for the whole animation.
+        bool dynamicColoringEnabled = false;
+        float startColors[4][3]; // Start colors of dynamic color transition.
+        float endColors[4][3];   // End colors of dynamic color transition.
     };
 
     // All callbacks will be called from this class's internal thread.
@@ -226,6 +234,7 @@
     GLuint mImageTextureLocation;
     GLuint mTextCropAreaLocation;
     GLuint mTextTextureLocation;
+    GLuint mImageColorProgressLocation;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ae812ec..bff1e57 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1105,18 +1105,17 @@
                 IUiAutomationConnection instrumentationUiConnection, int debugMode,
                 boolean enableBinderTracking, boolean trackAllocation,
                 boolean isRestrictedBackupMode, boolean persistent, Configuration config,
-                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
+                CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
                 String buildSerial, AutofillOptions autofillOptions,
                 ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
                 SharedMemory serializedSystemFontMap) {
             if (services != null) {
                 if (false) {
                     // Test code to make sure the app could see the passed-in services.
-                    for (Object oname : services.keySet()) {
-                        if (services.get(oname) == null) {
+                    for (String name : services.keySet()) {
+                        if (services.get(name) == null) {
                             continue; // AM just passed in a null service.
                         }
-                        String name = (String) oname;
 
                         // See b/79378449 about the following exemption.
                         switch (name) {
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d6ff6d3..2afd98e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -77,7 +77,7 @@
             IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
             boolean restrictedBackupMode, boolean persistent, in Configuration config,
-            in CompatibilityInfo compatInfo, in Map services,
+            in CompatibilityInfo compatInfo, in Map<String, IBinder> services,
             in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
             in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
             in SharedMemory serializedSystemFontMap);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4b401a5..7102314 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3270,22 +3270,6 @@
     }
 
     /**
-     * Returns true if the Profile Challenge is available to use for the given profile user.
-     *
-     * @hide
-     */
-    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
-        if (mService != null) {
-            try {
-                return mService.isSeparateProfileChallengeAllowed(userHandle);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-        return false;
-    }
-
-    /**
      * Constant for {@link #setPasswordQuality}: the policy has no requirements
      * for the password.  Note that quality constants are ordered so that higher
      * values are more restrictive.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b6c48a1..8cf1f80 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -373,8 +373,6 @@
     CharSequence getShortSupportMessageForUser(in ComponentName admin, int userHandle);
     CharSequence getLongSupportMessageForUser(in ComponentName admin, int userHandle);
 
-    boolean isSeparateProfileChallengeAllowed(int userHandle);
-
     void setOrganizationColor(in ComponentName admin, in int color);
     void setOrganizationColorForUser(in int color, in int userId);
     int getOrganizationColor(in ComponentName admin);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2ed26a9f..2b28c11 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -64,6 +64,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -78,6 +80,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -715,10 +718,21 @@
     private final IBluetoothManager mManagerService;
     private final AttributionSource mAttributionSource;
 
+    // Yeah, keeping both mService and sService isn't pretty, but it's too late
+    // in the current release for a major refactoring, so we leave them both
+    // intact until this can be cleaned up in a future release
+
     @UnsupportedAppUsage
+    @GuardedBy("mServiceLock")
     private IBluetooth mService;
     private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
+    @GuardedBy("sServiceLock")
+    private static boolean sServiceRegistered;
+    @GuardedBy("sServiceLock")
+    private static IBluetooth sService;
+    private static final Object sServiceLock = new Object();
+
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
     private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
@@ -792,19 +806,11 @@
      * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
      */
     BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
-        if (managerService == null) {
-            throw new IllegalArgumentException("bluetooth manager service is null");
-        }
-        try {
-            mServiceLock.writeLock().lock();
-            mService = managerService.registerAdapter(mManagerCallback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.writeLock().unlock();
-        }
         mManagerService = Objects.requireNonNull(managerService);
         mAttributionSource = Objects.requireNonNull(attributionSource);
+        synchronized (mServiceLock.writeLock()) {
+            mService = getBluetoothService(mManagerCallback);
+        }
         mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
         mToken = new Binder(DESCRIPTOR);
     }
@@ -3162,21 +3168,16 @@
         }
     }
 
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothManagerCallback mManagerCallback =
+    private static final IBluetoothManagerCallback sManagerCallback =
             new IBluetoothManagerCallback.Stub() {
-                @SuppressLint("AndroidFrameworkRequiresPermission")
                 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
                     if (DBG) {
                         Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
                     }
 
-                    mServiceLock.writeLock().lock();
-                    mService = bluetoothService;
-                    mServiceLock.writeLock().unlock();
-
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) {
+                    synchronized (sServiceLock) {
+                        sService = bluetoothService;
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
                             try {
                                 if (cb != null) {
                                     cb.onBluetoothServiceUp(bluetoothService);
@@ -3188,6 +3189,56 @@
                             }
                         }
                     }
+                }
+
+                public void onBluetoothServiceDown() {
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceDown");
+                    }
+
+                    synchronized (sServiceLock) {
+                        sService = null;
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
+                            try {
+                                if (cb != null) {
+                                    cb.onBluetoothServiceDown();
+                                } else {
+                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "", e);
+                            }
+                        }
+                    }
+                }
+
+                public void onBrEdrDown() {
+                    if (VDBG) {
+                        Log.i(TAG, "onBrEdrDown");
+                    }
+
+                    synchronized (sServiceLock) {
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
+                            try {
+                                if (cb != null) {
+                                    cb.onBrEdrDown();
+                                } else {
+                                    Log.d(TAG, "onBrEdrDown: cb is null!");
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "", e);
+                            }
+                        }
+                    }
+                }
+            };
+
+    private final IBluetoothManagerCallback mManagerCallback =
+            new IBluetoothManagerCallback.Stub() {
+                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                    synchronized (mServiceLock.writeLock()) {
+                        mService = bluetoothService;
+                    }
                     synchronized (mMetadataListeners) {
                         mMetadataListeners.forEach((device, pair) -> {
                             try {
@@ -3212,12 +3263,7 @@
                 }
 
                 public void onBluetoothServiceDown() {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceDown: " + mService);
-                    }
-
-                    try {
-                        mServiceLock.writeLock().lock();
+                    synchronized (mServiceLock.writeLock()) {
                         mService = null;
                         if (mLeScanClients != null) {
                             mLeScanClients.clear();
@@ -3228,29 +3274,10 @@
                         if (mBluetoothLeScanner != null) {
                             mBluetoothLeScanner.cleanup();
                         }
-                    } finally {
-                        mServiceLock.writeLock().unlock();
-                    }
-
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceDown();
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
                     }
                 }
 
                 public void onBrEdrDown() {
-                    if (VDBG) {
-                        Log.i(TAG, "onBrEdrDown: " + mService);
-                    }
                 }
             };
 
@@ -3485,15 +3512,12 @@
 
     protected void finalize() throws Throwable {
         try {
-            mManagerService.unregisterAdapter(mManagerCallback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
+            removeServiceStateCallback(mManagerCallback);
         } finally {
             super.finalize();
         }
     }
 
-
     /**
      * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
      * <p>Alphabetic characters must be uppercase to be valid.
@@ -3557,24 +3581,64 @@
         return mAttributionSource;
     }
 
-    private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks =
-            new ArrayList<IBluetoothManagerCallback>();
+    @GuardedBy("sServiceLock")
+    private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks =
+            new WeakHashMap<>();
+
+    /*package*/ IBluetooth getBluetoothService() {
+        synchronized (sServiceLock) {
+            if (sProxyServiceStateCallbacks.isEmpty()) {
+                throw new IllegalStateException(
+                        "Anonymous service access requires at least one lifecycle in process");
+            }
+            return sService;
+        }
+    }
 
     @UnsupportedAppUsage
     /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
-        synchronized (mProxyServiceStateCallbacks) {
-            if (cb == null) {
-                Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback");
-            } else if (!mProxyServiceStateCallbacks.contains(cb)) {
-                mProxyServiceStateCallbacks.add(cb);
-            }
+        Objects.requireNonNull(cb);
+        synchronized (sServiceLock) {
+            sProxyServiceStateCallbacks.put(cb, null);
+            registerOrUnregisterAdapterLocked();
+            return sService;
         }
-        return mService;
     }
 
     /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
-        synchronized (mProxyServiceStateCallbacks) {
-            mProxyServiceStateCallbacks.remove(cb);
+        Objects.requireNonNull(cb);
+        synchronized (sServiceLock) {
+            sProxyServiceStateCallbacks.remove(cb);
+            registerOrUnregisterAdapterLocked();
+        }
+    }
+
+    /**
+     * Handle registering (or unregistering) a single process-wide
+     * {@link IBluetoothManagerCallback} based on the presence of local
+     * {@link #sProxyServiceStateCallbacks} clients.
+     */
+    @GuardedBy("sServiceLock")
+    private void registerOrUnregisterAdapterLocked() {
+        final boolean isRegistered = sServiceRegistered;
+        final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty();
+
+        if (isRegistered != wantRegistered) {
+            if (wantRegistered) {
+                try {
+                    sService = mManagerService.registerAdapter(sManagerCallback);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            } else {
+                try {
+                    mManagerService.unregisterAdapter(sManagerCallback);
+                    sService = null;
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            sServiceRegistered = wantRegistered;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index bb409d5..1655b62 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -399,7 +399,7 @@
         try {
             if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
             IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
             mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
                     mUuid, mPort, getSecurityFlags());
@@ -438,7 +438,7 @@
     /*package*/ int bindListen() {
         int ret;
         if (mSocketState == SocketState.CLOSED) return EBADFD;
-        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
         if (bluetoothProxy == null) {
             Log.e(TAG, "bindListen fail, reason: bluetooth is off");
             return -1;
@@ -706,7 +706,7 @@
                 throw new IOException("socket closed");
             }
             IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
             if (bluetoothProxy == null) {
                 throw new IOException("Bluetooth is off");
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 31a84ed..ad1163f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4470,11 +4470,11 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
-     * android.media.MediaTranscodeManager} for transcoding media.
+     * android.media.MediaTranscodingManager} for transcoding media.
      *
      * @hide
      * @see #getSystemService(String)
-     * @see android.media.MediaTranscodeManager
+     * @see android.media.MediaTranscodingManager
      */
     @SystemApi
     public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a5cd331..19db0ba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1077,8 +1077,7 @@
     public static final int ROLLBACK_DATA_POLICY_WIPE = 1;
 
     /**
-     * User data won't be backed up during install and won't be restored during rollback.
-     * TODO: Not implemented yet.
+     * User data won't be backed up during install and will remain unchanged during rollback.
      *
      * @hide
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7a031e3..57fd736 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10221,6 +10221,13 @@
                 "device_state_rotation_lock";
 
         /**
+         * Control whether communal mode is allowed on this device.
+         *
+         * @hide
+         */
+        public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
+
+        /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
          */
@@ -10367,6 +10374,14 @@
                 "enable_accessibility_global_gesture_enabled";
 
         /**
+         * Whether select sound track with audio description by default.
+         * @hide
+         */
+        @Readable
+        public static final String ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
+                "enable_accessibility_audio_description_by_default";
+
+        /**
          * Whether Airplane Mode is on.
          */
         @Readable
@@ -16895,6 +16910,14 @@
              * @hide
              */
             public static final String COMBINED_LOCATION_ENABLED = "combined_location_enable";
+
+            /**
+             * The wrist orientation mode of the device
+             * Valid values - LEFT_WRIST_ROTATION_0 = "0" (default), LEFT_WRIST_ROTATION_180 = "1",
+             *          RIGHT_WRIST_ROTATION_0 = "2", RIGHT_WRIST_ROTATION_180 = "3"
+             * @hide
+             */
+            public static final String WRIST_ORIENTATION_MODE = "wear_wrist_orientation_mode";
         }
     }
 
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4edff27..f50b51b 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -27,6 +27,7 @@
 import java.util.ConcurrentModificationException;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 /**
  * ArrayMap is a generic key->value mapping data structure that is
@@ -980,6 +981,28 @@
     }
 
     /**
+     * Performs the given action for all elements in the stored order. This implementation overrides
+     * the default implementation to avoid iterating using the {@link #entrySet()} and iterates in
+     * the key-value order consistent with {@link #keyAt(int)} and {@link #valueAt(int)}.
+     *
+     * @param action The action to be performed for each element
+     */
+    @Override
+    public void forEach(BiConsumer<? super K, ? super V> action) {
+        if (action == null) {
+            throw new NullPointerException("action must not be null");
+        }
+
+        final int size = mSize;
+        for (int i = 0; i < size; ++i) {
+            if (size != mSize) {
+                throw new ConcurrentModificationException();
+            }
+            action.accept(keyAt(i), valueAt(i));
+        }
+    }
+
+    /**
      * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var>
      * @param map The map whose contents are to be retrieved.
      */
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index c16ee42..7d2cb50 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -789,6 +789,9 @@
             } else {
                 mDisplayResolveInfo.setDisplayIcon(d);
                 mHolder.bindIcon(mDisplayResolveInfo);
+                // Notify in case view is already bound to resolve the race conditions on
+                // low end devices
+                notifyDataSetChanged();
             }
         }
 
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 10750b6..0c56c67 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -64,8 +64,7 @@
     public static final String LIB_DIR_NAME = "lib";
     public static final String LIB64_DIR_NAME = "lib64";
 
-    // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
-    // that the cpuAbiOverride must be clear.
+    // Special value for indicating that the cpuAbiOverride must be clear.
     public static final String CLEAR_ABI_OVERRIDE = "-";
 
     /**
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 498505c..dbf4528 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -846,14 +846,6 @@
         return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
     }
 
-    /**
-     * Retrieves whether the current DPM allows use of the Profile Challenge.
-     */
-    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
-        return isManagedProfile(userHandle)
-                && getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle);
-    }
-
     private boolean hasSeparateChallenge(int userHandle) {
         try {
             return getLockSettings().getSeparateProfileChallengeEnabled(userHandle);
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 4fc135c..627381c 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -66,15 +66,15 @@
         private float[] mTraceY = new float[32];
         private boolean[] mTraceCurrent = new boolean[32];
         private int mTraceCount;
-        
+
         // True if the pointer is down.
         @UnsupportedAppUsage
         private boolean mCurDown;
-        
+
         // Most recent coordinates.
         private PointerCoords mCoords = new PointerCoords();
         private int mToolType;
-        
+
         // Most recent velocity.
         private float mXVelocity;
         private float mYVelocity;
@@ -107,7 +107,7 @@
                 float[] newTraceX = new float[traceCapacity];
                 System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
                 mTraceX = newTraceX;
-                
+
                 float[] newTraceY = new float[traceCapacity];
                 System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
                 mTraceY = newTraceY;
@@ -116,7 +116,7 @@
                 System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
                 mTraceCurrent= newTraceCurrent;
             }
-            
+
             mTraceX[mTraceCount] = x;
             mTraceY[mTraceCount] = y;
             mTraceCurrent[mTraceCount] = current;
@@ -162,7 +162,7 @@
 
     @UnsupportedAppUsage
     private boolean mPrintCoords = true;
-    
+
     public PointerLocationView(Context c) {
         super(c);
         setFocusableInTouchMode(true);
@@ -211,7 +211,7 @@
         PointerState ps = new PointerState();
         mPointers.add(ps);
         mActivePointerId = 0;
-        
+
         mVelocity = VelocityTracker.obtain();
 
         String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
@@ -252,7 +252,7 @@
                     + " bottom=" + mTextMetrics.bottom);
         }
     }
-    
+
     // Draw an oval.  When angle is 0 radians, orients the major axis vertically,
     // angles less than or greater than 0 radians rotate the major axis left or right.
     private RectF mReusableOvalRect = new RectF();
@@ -399,7 +399,7 @@
     }
 
     private void drawLabels(Canvas canvas) {
-        if (mActivePointerId < 0) {
+        if (mActivePointerId < 0 || mActivePointerId >= mPointers.size()) {
             return;
         }
 
@@ -614,8 +614,8 @@
                 NP++;
             }
 
-            if (mActivePointerId < 0 ||
-                    !mPointers.get(mActivePointerId).mCurDown) {
+            if (mActivePointerId < 0 || mActivePointerId >= NP
+                    || !mPointers.get(mActivePointerId).mCurDown) {
                 mActivePointerId = id;
             }
 
@@ -710,7 +710,7 @@
 
         invalidate();
     }
-    
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         onPointerEvent(event);
@@ -862,16 +862,16 @@
     private static final class FasterStringBuilder {
         private char[] mChars;
         private int mLength;
-        
+
         public FasterStringBuilder() {
             mChars = new char[64];
         }
-        
+
         public FasterStringBuilder clear() {
             mLength = 0;
             return this;
         }
-        
+
         public FasterStringBuilder append(String value) {
             final int valueLength = value.length();
             final int index = reserve(valueLength);
@@ -879,11 +879,11 @@
             mLength += valueLength;
             return this;
         }
-        
+
         public FasterStringBuilder append(int value) {
             return append(value, 0);
         }
-        
+
         public FasterStringBuilder append(int value, int zeroPadWidth) {
             final boolean negative = value < 0;
             if (negative) {
@@ -893,16 +893,16 @@
                     return this;
                 }
             }
-            
+
             int index = reserve(11);
             final char[] chars = mChars;
-            
+
             if (value == 0) {
                 chars[index++] = '0';
                 mLength += 1;
                 return this;
             }
-            
+
             if (negative) {
                 chars[index++] = '-';
             }
@@ -916,18 +916,18 @@
                     chars[index++] = '0';
                 }
             }
-            
+
             do {
                 int digit = value / divisor;
                 value -= digit * divisor;
                 divisor /= 10;
                 chars[index++] = (char) (digit + '0');
             } while (divisor != 0);
-            
+
             mLength = index;
             return this;
         }
-        
+
         public FasterStringBuilder append(float value, int precision) {
             int scale = 1;
             for (int i = 0; i < precision; i++) {
@@ -947,15 +947,15 @@
                 value -= Math.floor(value);
                 append((int) (value * scale), precision);
             }
-            
+
             return this;
         }
-        
+
         @Override
         public String toString() {
             return new String(mChars, 0, mLength);
         }
-        
+
         private int reserve(int length) {
             final int oldLength = mLength;
             final int newLength = mLength + length;
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 45f8c132..97097ff 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -72,6 +72,7 @@
     optional bool adb_enabled = 14;
     optional string kernel_state = 15;
     optional string kernel_function_list = 16;
+    optional string uevent = 17;
 }
 
 message UsbAccessoryProto {
@@ -315,6 +316,7 @@
     optional int32 parent_user_id = 1;
     repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
     repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+    optional string intent = 4;
 }
 
 message UsbSettingsDevicePreferenceProto {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 29e9917..363a4b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -375,7 +375,7 @@
                 if (options == null) {
                     options = new Bundle();
                 }
-                updateActivityOptions(options, position, wct);
+                updateActivityOptions(options, position);
                 break;
             }
             case STAGE_TYPE_MAIN: {
@@ -390,7 +390,7 @@
                 if (options == null) {
                     options = new Bundle();
                 }
-                updateActivityOptions(options, position, wct);
+                updateActivityOptions(options, position);
                 break;
             }
             default:
@@ -491,22 +491,8 @@
         opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
     }
 
-    void updateActivityOptions(Bundle opts, @SplitPosition int position,
-            @Nullable WindowContainerTransaction wct) {
+    void updateActivityOptions(Bundle opts, @SplitPosition int position) {
         addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
-
-        if (!mMainStage.isActive()) {
-            // Activate the main stage in anticipation of an app launch.
-            boolean needsApply = wct == null;
-            if (needsApply) {
-                wct = new WindowContainerTransaction();
-            }
-            mMainStage.activate(getMainStageBounds(), wct);
-            mSideStage.setBounds(getSideStageBounds(), wct);
-            if (needsApply) {
-                mTaskOrganizer.applyTransaction(wct);
-            }
-        }
     }
 
     void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index c09fdc2..050beb3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -31,7 +31,10 @@
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
+import org.junit.Assume.assumeFalse
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,6 +54,12 @@
     private val imeApp = ImeAppHelper(instrumentation)
     private val testApp = FixedAppHelper(instrumentation)
 
+    @Before
+    open fun setup() {
+        // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+        assumeFalse(isShellTransitionsEnabled())
+    }
+
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             withTestName { testSpec.name }
diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java
index 718977f..5493e25 100644
--- a/location/java/android/location/CorrelationVector.java
+++ b/location/java/android/location/CorrelationVector.java
@@ -94,8 +94,6 @@
                 "FrequencyOffsetMetersPerSecond must be non-negative (greater than or equal to 0)");
         Preconditions.checkArgument(builder.mSamplingWidthMeters > 0.0,
                 "SamplingWidthMeters must be positive (greater than 0)");
-        Preconditions.checkArgument(builder.mSamplingStartMeters >= 0.0,
-                "SamplingStartMeters must be non-negative (greater than or equal to 0)");
         mMagnitude = builder.mMagnitude;
         mFrequencyOffsetMetersPerSecond = builder.mFrequencyOffsetMetersPerSecond;
         mSamplingWidthMeters = builder.mSamplingWidthMeters;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index d1c4b34..a90a38b 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -641,7 +641,7 @@
 
     if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0) {
         sp<MediaEvent> mediaEventSp =
-                new MediaEvent(mFilterClient, makeFromAidl(mediaEvent.avMemory),
+                new MediaEvent(mFilterClient, dupFromAidl(mediaEvent.avMemory),
                                mediaEvent.avDataId, dataLength + offset, obj);
         mediaEventSp->mAvHandleRefCnt++;
         env->SetLongField(obj, eventContext, (jlong)mediaEventSp.get());
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index eea27ea..d4d8837 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -160,7 +160,7 @@
 
 Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) {
     if (mTunerFilter != nullptr) {
-        Status s = mTunerFilter->releaseAvHandle(makeToAidl(handle), avDataId);
+        Status s = mTunerFilter->releaseAvHandle(dupToAidl(handle), avDataId);
         return ClientHelper::getServiceSpecificErrorCode(s);
     }
 
@@ -308,7 +308,7 @@
         NativeHandle avMemory;
         Status s = mTunerFilter->getAvSharedHandle(&avMemory, &size);
         if (s.isOk()) {
-            mAvSharedHandle = native_handle_clone(makeFromAidl(avMemory));
+            mAvSharedHandle = dupFromAidl(avMemory);
             mAvSharedMemSize = size;
         }
     }
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
index e74ac44..fede44f 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
@@ -33,18 +33,18 @@
     <style name="Banner.Title.SettingsLib"
         parent="@android:style/TextAppearance.Material.Subhead">
         <item name="android:textSize">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="Banner.Subtitle.SettingsLib"
-        parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+        parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
 
     <style name="Banner.Summary.SettingsLib"
-        parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+        parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
@@ -58,4 +58,4 @@
         parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
index df47c64..4c6ed58 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
@@ -17,14 +17,13 @@
 
 <resources>
     <style name="Banner.Text.Title"
-           parent="@android:style/TextAppearance.Material.Subhead">
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textSize">16sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="Banner.Text.Summary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
index 92514ad..1c44207 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -87,9 +87,9 @@
     </style>
 
     <style name="BarChart.Text"
-           parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">16sp</item>
     </style>
 
     <style name="BarChart.Text.HeaderTitle">
@@ -101,7 +101,7 @@
     </style>
 
     <style name="BarChart.Text.Summary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">12sp</item>
     </style>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 2f911c4..238e65e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -19,6 +19,7 @@
         "com.google.android.material_material",
         "SettingsLibSettingsTransition",
         "SettingsLibUtils",
+        "SettingsLibSettingsTheme",
     ],
     sdk_version: "system_current",
     min_sdk_version: "29",
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index 5950656..907863e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -16,7 +16,6 @@
   -->
 <androidx.coordinatorlayout.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/content_parent"
     android:layout_width="match_parent"
@@ -40,7 +39,7 @@
             android:clipToPadding="false"
             app:forceApplySystemWindowInsetTop="true"
             app:extraMultilineHeightEnabled="true"
-            app:contentScrim="?androidprv:attr/colorSurfaceHeader"
+            app:contentScrim="@color/settingslib_colorSurfaceHeader"
             app:maxLines="3"
             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
             app:scrimAnimationDuration="50"
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
similarity index 83%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
index 878275a..c20beaf 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
@@ -18,7 +18,7 @@
     <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
         <item name="elevationOverlayEnabled">true</item>
         <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
-        <item name="colorAccent">@*android:color/accent_device_default_dark</item>
+        <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
similarity index 91%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
index 63d397c..bda51b5 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
@@ -16,7 +16,7 @@
 -->
 <resources>
     <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20dp</item>
     </style>
 
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
similarity index 82%
copy from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
copy to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
index 878275a..9ecc297 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
@@ -18,7 +18,7 @@
     <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
         <item name="elevationOverlayEnabled">true</item>
         <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
-        <item name="colorAccent">@*android:color/accent_device_default_dark</item>
+        <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_light</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
deleted file mode 100644
index 2e7a6a9..0000000
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2021 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.
--->
-<resources>
-    <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
-        <item name="elevationOverlayEnabled">true</item>
-        <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
-        <item name="colorAccent">@*android:color/accent_device_default_light</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/FooterPreference/res/values/styles.xml b/packages/SettingsLib/FooterPreference/res/values/styles.xml
index 08dd359..5a3bada 100644
--- a/packages/SettingsLib/FooterPreference/res/values/styles.xml
+++ b/packages/SettingsLib/FooterPreference/res/values/styles.xml
@@ -17,9 +17,8 @@
 
 <resources>
     <style name="TextAppearance.Footer.Title.SettingsLib"
-           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textSize">14sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 4a99e84..2ffe6d9 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -24,14 +24,13 @@
     </style>
 
     <style name="TextAppearance.EntityHeaderTitle"
-           parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">20sp</item>
     </style>
 
     <style name="TextAppearance.EntityHeaderSummary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textAlignment">viewStart</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:singleLine">true</item>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 6e5911c..30748e6 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -50,7 +50,7 @@
             android:tint="?android:attr/colorAccent"
             android:layout_gravity="center_vertical"
             android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
-            android:src="@*android:drawable/ic_info"
+            android:src="@android:drawable/ic_info"
             android:visibility="gone" />
 
         <Switch
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index 306145a..d0c2d0b 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -28,7 +28,7 @@
         android:layout_gravity="center_vertical"
         android:maxLines="2"
         android:ellipsize="end"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"
+        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"
         android:textSize="16sp"
         android:textColor="?android:attr/textColorPrimaryInverse"
         android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
@@ -42,7 +42,7 @@
         android:theme="@android:style/Theme.Material"
         android:layout_gravity="center_vertical"
         android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
-        android:src="@*android:drawable/ic_info"
+        android:src="@android:drawable/ic_info"
         android:visibility="gone"/>
 
     <Switch
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
new file mode 100644
index 0000000..2272a37
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+
+<resources>
+
+    <!-- Size of layout margin -->
+    <dimen name="settingslib_switchbar_margin">16dp</dimen>
+
+    <!-- Size of layout margin left -->
+    <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
+
+    <!-- Size of layout margin right -->
+    <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
+
+    <!-- Minimum width of switch -->
+    <dimen name="settingslib_min_switch_width">52dp</dimen>
+
+    <!-- Minimum width of switch bar -->
+    <dimen name="settingslib_min_switch_bar_height">72dp</dimen>
+
+    <!-- Radius of switch bar -->
+    <dimen name="settingslib_switch_bar_radius">28dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
new file mode 100644
index 0000000..a50fc7c
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2021 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.
+  -->
+
+<resources>
+
+    <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+        <item name="android:textSize">20sp</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 16b8af6..6362882 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (C) 2020 The Android Open Source Project
+  Copyright (C) 2021 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.
@@ -17,30 +17,12 @@
 
 <resources>
 
-    <!-- Size of layout margin -->
-    <dimen name="settingslib_switchbar_margin">16dp</dimen>
-
-    <!-- Size of layout margin left -->
-    <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
-
-    <!-- Size of layout margin right -->
-    <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
-
-    <!-- Minimum width of switch -->
-    <dimen name="settingslib_min_switch_width">52dp</dimen>
-
-    <!-- Minimum width of switch bar -->
-    <dimen name="settingslib_min_switch_bar_height">72dp</dimen>
-
     <!-- Restricted icon size in switch bar -->
-    <dimen name="settingslib_restricted_icon_size">@*android:dimen/config_restrictedIconSize</dimen>
+    <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>
 
     <!-- Restricted icon in switch bar -->
     <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen>
 
-    <!-- Radius of switch bar -->
-    <dimen name="settingslib_switch_bar_radius">28dp</dimen>
-
     <!-- Size of title margin -->
     <dimen name="settingslib_switch_title_margin">16dp</dimen>
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
index 3924e30..870812a 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-   Copyright (C) 2020 The Android Open Source Project
+   Copyright (C) 2021 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.
@@ -17,13 +17,6 @@
 
 <resources>
 
-    <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:textSize">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">@android:color/black</item>
-    </style>
-
-
     <style name="SwitchBar.Switch.Settingslib" parent="@android:style/Widget.Material.CompoundButton.Switch">
         <item name="android:trackTint">@color/settingslib_switchbar_switch_track_tint</item>
         <item name="android:thumbTint">@color/settingslib_switchbar_switch_thumb_tint</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
new file mode 100644
index 0000000..037b80a
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_500" android:lStar="98" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 8c7c7ed..e8fd4b2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -36,4 +36,9 @@
     <color name="settingslib_dialog_colorError">#f28b82</color> <!-- Red 300 -->
 
     <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral1_700</color>
+
+    <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_700</color>
+
+    <!-- copy from accent_primary_variant_dark_device_default-->
+    <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 77f1bcd..0f20ec3 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -37,9 +37,30 @@
     <!-- Dialog accent color -->
     <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color>
     <!-- Dialog background color -->
-    <color name="settingslib_dialog_background">@*android:color/surface_light</color>
+    <color name="settingslib_dialog_background">@color/settingslib_surface_light</color>
     <!-- Dialog error color. -->
     <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 -->
 
     <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral2_100</color>
+
+    <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_100</color>
+
+    <color name="settingslib_accent_device_default_dark">@android:color/system_accent1_100</color>
+
+    <color name="settingslib_accent_device_default_light">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_primary_dark_device_default_settings">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_primary_device_default_settings_light">@android:color/system_neutral1_50</color>
+
+    <color name="settingslib_accent_primary_device_default">@android:color/system_accent1_100</color>
+
+    <!-- copy from accent_primary_variant_light_device_default-->
+    <color name="settingslib_accent_primary_variant">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_accent_secondary_device_default">@android:color/system_accent2_100</color>
+
+    <color name="settingslib_background_device_default_dark">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index ddcc83e..1c33f1a 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -19,4 +19,5 @@
     <dimen name="app_preference_padding_start">20dp</dimen>
     <dimen name="app_icon_min_width">52dp</dimen>
     <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen>
+    <dimen name="settingslib_dialogCornerRadius">28dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
new file mode 100644
index 0000000..6d072a9
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Name of a font family to use for headlines in SettingsLib. -->
+    <string name="settingslib_config_headlineFontFamily" translatable="false">
+        @*android:string/config_headlineFontFamily
+    </string>
+
+    <!-- Name of a font family to use for headlines-medium in SettingsLib. -->
+    <string name="settingslib_config_headlineFontFamilyMedium" translatable="false">
+        @*android:string/config_headlineFontFamilyMedium
+    </string>
+
+    <!-- Name of a font family to use for body in SettingsLib. -->
+    <string name="settingslib_config_bodyFontFamily" translatable="false">
+        @*android:string/config_bodyFontFamily
+    </string>
+
+    <!-- Name of a font family to use for body-medium in SettingsLib. -->
+    <string name="settingslib_config_bodyFontFamilyMedium" translatable="false">
+        @*android:string/config_bodyFontFamilyMedium
+    </string>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 46f1e03..5800636 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -16,12 +16,16 @@
   -->
 <resources>
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
-           parent="@*android:style/TextAppearance.DeviceDefault.ListItem">
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
 
     <style name="TextAppearance.CategoryTitle.SettingsLib"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body2" />
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
 
     <style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch">
         <item name="android:switchMinWidth">52dp</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 8034710..6bf288b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -50,6 +50,6 @@
         <item name="android:clipToPadding">true</item>
         <item name="android:clipChildren">true</item>
 
-        <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
+        <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
index 25f9514..18af1f9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
@@ -20,4 +20,5 @@
     <dimen name="app_preference_padding_start">?android:attr/listPreferredItemPaddingStart</dimen>
     <dimen name="app_icon_min_width">56dp</dimen>
     <dimen name="two_target_min_width">72dp</dimen>
+    <dimen name="settingslib_dialogCornerRadius">8dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 6f25177..2d881d1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -35,7 +35,7 @@
         <!-- TODO(b/189308264): fix the crash in Android R if set the attributes:
              <item name="colorAccent">@*android:color/accent_device_default_light</item>
              <item name="android:colorBackground">@color/settingslib_dialog_background</item>
-             <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
+             <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item>
         -->
         <item name="android:windowSoftInputMode">adjustResize</item>
         <item name="android:clipToPadding">true</item>
diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
index 65869b5..b6ca41f 100644
--- a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
@@ -16,8 +16,7 @@
   -->
 <resources>
     <style name="TextAppearance.TopIntroText"
-           parent="@*android:style/TextAppearance.DeviceDefault">
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 183a06c..cab7cee 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -20,68 +20,68 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Toast message when Wi-Fi cannot scan for networks -->
     <string name="wifi_fail_to_scan">Can\'t scan for networks</string>
-    <!-- Concise terminology for wifi with WEP security -->
-    <string name="wifi_security_short_wep" translatable="false">WEP</string>
-    <!-- Concise terminology for wifi with WPA security -->
-    <string name="wifi_security_short_wpa" translatable="false">WPA</string>
-    <!-- Concise terminology for wifi with WPA2 security -->
-    <string name="wifi_security_short_wpa2" translatable="false">WPA2</string>
-    <!-- Concise terminology for wifi with both WPA/WPA2 security -->
-    <string name="wifi_security_short_wpa_wpa2" translatable="false">WPA/WPA2</string>
+    <!-- Concise terminology for wifi with WEP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_wep">WEP</string>
+    <!-- Concise terminology for wifi with WPA security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_wpa">WPA</string>
+    <!-- Concise terminology for wifi with WPA2 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_wpa2">WPA2</string>
+    <!-- Concise terminology for wifi with both WPA/WPA2 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_wpa_wpa2">WPA/WPA2</string>
     <!-- Concise terminology for wifi with unknown PSK type -->
     <string name="wifi_security_short_psk_generic" translatable="false">@string/wifi_security_short_wpa_wpa2</string>
-    <!-- Concise terminology for wifi with 802.1x EAP security -->
-    <string name="wifi_security_short_eap" translatable="false">802.1x</string>
-    <!-- Concise terminology for wifi with WPA 802.1x EAP security -->
-    <string name="wifi_security_short_eap_wpa" translatable="false">WPA-EAP</string>
-    <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
-    <string name="wifi_security_short_eap_wpa2_wpa3" translatable="false">RSN-EAP</string>
-    <!-- Concise terminology for wifi with WPA3 security -->
-    <string name="wifi_security_short_sae" translatable="false">WPA3</string>
-    <!-- Concise terminology for wifi with WPA2/WPA3 transition security -->
-    <string name="wifi_security_short_psk_sae" translatable="false">WPA2/WPA3</string>
-    <!-- Concise terminology for Wi-Fi with None/OWE transition mode security -->
-    <string name="wifi_security_short_none_owe" translatable="false">None/OWE</string>
-    <!-- Concise terminology for wifi with OWE security -->
-    <string name="wifi_security_short_owe" translatable="false">OWE</string>
-    <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
-    <string name="wifi_security_short_eap_suiteb" translatable="false">Suite-B-192</string>
+    <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_eap">802.1x</string>
+    <!-- Concise terminology for wifi with WPA 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_eap_wpa">WPA-EAP</string>
+    <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_eap_wpa2_wpa3">RSN-EAP</string>
+    <!-- Concise terminology for wifi with WPA3 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_sae">WPA3</string>
+    <!-- Concise terminology for wifi with WPA2/WPA3 transition security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_psk_sae">WPA2/WPA3</string>
+    <!-- Concise terminology for Wi-Fi with None/OWE transition mode security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_none_owe">None/OWE</string>
+    <!-- Concise terminology for wifi with OWE security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_owe">OWE</string>
+    <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_short_eap_suiteb">Suite-B-192</string>
 
     <!-- Used in Wi-Fi settings dialogs when Wi-Fi does not have any security. [CHAR LIMIT=40] -->
     <string name="wifi_security_none">None</string>
 
-    <!-- Terminology for wifi with WEP security -->
-    <string name="wifi_security_wep" translatable="false">WEP</string>
-    <!-- Terminology for wifi with WPA security -->
-    <string name="wifi_security_wpa" translatable="false">WPA-Personal</string>
-    <!-- Terminology for wifi with WPA2 security -->
-    <string name="wifi_security_wpa2" translatable="false">WPA2-Personal</string>
-    <!-- Terminology for wifi with both WPA/WPA2 security, or unknown -->
-    <string name="wifi_security_wpa_wpa2" translatable="false">WPA/WPA2-Personal</string>
+    <!-- Terminology for wifi with WEP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_wep">WEP</string>
+    <!-- Terminology for wifi with WPA security [CHAR LIMIT=40] -->
+    <string name="wifi_security_wpa">WPA-Personal</string>
+    <!-- Terminology for wifi with WPA2 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_wpa2">WPA2-Personal</string>
+    <!-- Terminology for wifi with both WPA/WPA2 security, or unknown [CHAR LIMIT=40] -->
+    <string name="wifi_security_wpa_wpa2">WPA/WPA2-Personal</string>
     <!-- Terminology for wifi with unknown PSK type -->
     <string name="wifi_security_psk_generic" translatable="false">@string/wifi_security_wpa_wpa2</string>
-    <!-- Concise terminology for wifi with 802.1x EAP security -->
-    <string name="wifi_security_eap" translatable="false">WPA/WPA2/WPA3-Enterprise</string>
-    <!-- Concise terminology for wifi with WPA 802.1x EAP security -->
-    <string name="wifi_security_eap_wpa" translatable="false">WPA-Enterprise</string>
-    <!-- Concise terminology for wifi with 802.1x EAP security -->
-    <string name="wifi_security_eap_wpa_wpa2" translatable="false">WPA/WPA2-Enterprise</string>
-    <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
-    <string name="wifi_security_eap_wpa2_wpa3" translatable="false">WPA2/WPA3-Enterprise</string>
-    <!-- Concise terminology for wifi with WPA3 802.1x EAP security -->
-    <string name="wifi_security_eap_wpa3" translatable="false">WPA3-Enterprise</string>
-    <!-- Concise terminology for Passpoint network -->
-    <string name="wifi_security_passpoint" translatable="false">Passpoint</string>
-    <!-- Terminology for wifi with WPA3 security -->
-    <string name="wifi_security_sae" translatable="false">WPA3-Personal</string>
-    <!-- Terminology for wifi with WPA2/WPA3 Transition mode security -->
-    <string name="wifi_security_psk_sae" translatable="false">WPA2/WPA3-Personal</string>
-    <!-- Terminology for Wi-Fi with None/OWE transition mode security -->
-    <string name="wifi_security_none_owe" translatable="false">None/Enhanced Open</string>
-    <!-- Terminology for wifi with OWE security -->
-    <string name="wifi_security_owe" translatable="false">Enhanced Open</string>
-    <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
-    <string name="wifi_security_eap_suiteb" translatable="false">WPA3-Enterprise 192-bit</string>
+    <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap">WPA/WPA2/WPA3-Enterprise</string>
+    <!-- Concise terminology for wifi with WPA 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap_wpa">WPA-Enterprise</string>
+    <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap_wpa_wpa2">WPA/WPA2-Enterprise</string>
+    <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap_wpa2_wpa3">WPA2/WPA3-Enterprise</string>
+    <!-- Concise terminology for wifi with WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap_wpa3">WPA3-Enterprise</string>
+    <!-- Concise terminology for Passpoint network [CHAR LIMIT=40] -->
+    <string name="wifi_security_passpoint">Passpoint</string>
+    <!-- Terminology for wifi with WPA3 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_sae">WPA3-Personal</string>
+    <!-- Terminology for wifi with WPA2/WPA3 Transition mode security [CHAR LIMIT=40] -->
+    <string name="wifi_security_psk_sae">WPA2/WPA3-Personal</string>
+    <!-- Terminology for Wi-Fi with None/OWE transition mode security [CHAR LIMIT=40] -->
+    <string name="wifi_security_none_owe">None/Enhanced Open</string>
+    <!-- Terminology for wifi with OWE security [CHAR LIMIT=40] -->
+    <string name="wifi_security_owe">Enhanced Open</string>
+    <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security [CHAR LIMIT=40] -->
+    <string name="wifi_security_eap_suiteb">WPA3-Enterprise 192-bit</string>
 
     <!-- Summary for the remembered network. -->
     <string name="wifi_remembered">Saved</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 364698f..2c0162f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -68,7 +68,6 @@
             Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
             final Intent intent = new Intent(ACTION_LEARN_MORE)
                     .putExtra(EXTRA_SETTING_KEY, EXTRA_SETTING_VALUE)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                     .setPackage(enforcedAdmin.component.getPackageName());
             context.startActivity(intent);
         };
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index fe48575..3ff1e48 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -317,6 +317,8 @@
                 NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.Wearable.BURN_IN_PROTECTION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.COMBINED_LOCATION_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.WRIST_ORIENTATION_MODE,
+                       new DiscreteValueValidator(new String[] {"0", "1", "2", "3"}));
     }
 }
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b210062..4f27de1 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -272,6 +272,7 @@
                     Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
                     Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
                     Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+                    Settings.Global.ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
                     Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
                     Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
                     Settings.Global.ENHANCED_4G_MODE_ENABLED,
@@ -665,7 +666,8 @@
                     Settings.Global.Wearable.MASTER_GESTURES_ENABLED,
                     Settings.Global.Wearable.UNGAZE_ENABLED,
                     Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS,
-                    Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED);
+                    Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED,
+                    Settings.Global.Wearable.WRIST_ORIENTATION_MODE);
 
     private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ea769c6..871b1c4 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -62,8 +62,8 @@
         <item name="android:src">@drawable/ic_backspace_24dp</item>
     </style>
     <style name="NumPadKey.Enter">
-      <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
-      <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
+        <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+        <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
     </style>
     <style name="Widget.TextView.NumPadKey.Klondike"
            parent="@android:style/Widget.DeviceDefault.TextView">
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 5b58fe8..3aa2e5a 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -191,6 +191,7 @@
                         android:layout_width="wrap_content"
                         android:layout_height="match_parent">
                         <TextView
+                            android:id="@+id/wifi_toggle_title"
                             android:text="@string/turn_on_wifi"
                             android:textDirection="locale"
                             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a4c1d94..38af659 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -69,6 +69,9 @@
     <!-- Shadows under the clock, date and other keyguard text fields -->
     <color name="keyguard_shadow_color">#B2000000</color>
 
+    <!-- Color for the images in keyguard number pad buttons   -->
+    <color name="keyguard_keypad_image_color">@android:color/background_light</color>
+
     <!-- Color for rounded background for activated user in keyguard user switcher -->
     <color name="kg_user_switcher_activated_background_color">#26000000</color>
     <!-- Icon color for user avatars in keyguard user switcher -->
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 509ac8a..aa8cbd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -46,6 +46,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.biometrics.UdfpsController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
@@ -99,6 +100,7 @@
     @NonNull private CharSequence mUnlockedLabel;
     @NonNull private CharSequence mLockedLabel;
     @Nullable private final Vibrator mVibrator;
+    @Nullable private final AuthRippleController mAuthRippleController;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
@@ -135,7 +137,8 @@
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull ConfigurationController configurationController,
             @NonNull @Main DelayableExecutor executor,
-            @Nullable Vibrator vibrator
+            @Nullable Vibrator vibrator,
+            @Nullable AuthRippleController authRippleController
     ) {
         super(view);
         mStatusBarStateController = statusBarStateController;
@@ -148,6 +151,7 @@
         mConfigurationController = configurationController;
         mExecutor = executor;
         mVibrator = vibrator;
+        mAuthRippleController = authRippleController;
 
         final Context context = view.getContext();
         mUnlockIcon = mView.getContext().getResources().getDrawable(
@@ -538,6 +542,9 @@
 
                     // pre-emptively set to true to hide view
                     mIsBouncerShowing = true;
+                    if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+                        mAuthRippleController.showRipple(FINGERPRINT);
+                    }
                     updateVisibility();
                     mKeyguardViewController.showBouncer(/* scrim */ true);
                 }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 99f9558..c659bf7 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -26,7 +26,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.settingslib.Utils;
+import com.android.systemui.R;
 
 /**
  * Similar to the {@link NumPadKey}, but displays an image.
@@ -92,8 +92,7 @@
     public void reloadColors() {
         if (mAnimator != null) mAnimator.reloadColors(getContext());
 
-        int textColor = Utils.getColorAttrDefaultColor(getContext(),
-                android.R.attr.colorBackground);
-        ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
+        int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color);
+        ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
index 81a13a2..4082015 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
@@ -57,7 +57,9 @@
         public void run() {
             mView.removeCallbacks(this);
             mView.show(false /* show */, true /* animate */, () -> {
-                mWindowManager.removeView(mView);
+                if (mView.isAttachedToWindow()) {
+                    mWindowManager.removeView(mView);
+                }
             });
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
index 4664423..b7e2982 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
@@ -107,12 +107,6 @@
 
     @Override
     protected void onViewDetached() {
-        destroy();
-    }
-
-    @Override
-    public void destroy() {
-        super.destroy();
         mConfigurationController.removeCallback(mConfigurationListener);
         unsubscribeFromTunerUpdates();
         mCurrentUserTracker.stopTracking();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 1df8ad5..66b45e5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -79,7 +79,7 @@
         notificationShadeWindowController.setForcePluginOpen(false, this)
     }
 
-    private fun showRipple(biometricSourceType: BiometricSourceType?) {
+    fun showRipple(biometricSourceType: BiometricSourceType?) {
         if (!keyguardUpdateMonitor.isKeyguardVisible ||
             keyguardUpdateMonitor.userNeedsStrongAuth()) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index bfa4780..5b327bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -34,7 +34,7 @@
  * See [DumpHandler] for more information on how and when this information is dumped.
  */
 @SysUISingleton
-class DumpManager @Inject constructor() {
+open class DumpManager @Inject constructor() {
     private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
     private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 28f63b0..6561bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -25,8 +25,12 @@
  * Proxy to make {@link SystemProperties} easily testable.
  */
 @SysUISingleton
-class SystemPropertiesHelper @Inject constructor() {
+open class SystemPropertiesHelper @Inject constructor() {
     fun getBoolean(name: String, default: Boolean): Boolean {
         return SystemProperties.getBoolean(name, default)
     }
+
+    fun set(name: String, value: Int) {
+        SystemProperties.set(name, value.toString())
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index e1e0ba7..8a3f983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -111,6 +111,7 @@
     private LinearLayout mMobileNetworkLayout;
     private LinearLayout mMobileNetworkList;
     private LinearLayout mTurnWifiOnLayout;
+    private TextView mWifiToggleTitleText;
     private LinearLayout mSeeAllLayout;
     private RecyclerView mWifiRecyclerView;
     private ImageView mConnectedWifiIcon;
@@ -215,6 +216,7 @@
         mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
         mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
         mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
         mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
         mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
         mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
@@ -368,6 +370,12 @@
 
     private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
         mWiFiToggle.setChecked(isWifiEnabled);
+        if (isDeviceLocked && mInternetDialogController.isNightMode()) {
+            int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
+                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+                    mContext, android.R.attr.textColorPrimary);
+            mWifiToggleTitleText.setTextColor(titleColor);
+        }
         mTurnWifiOnLayout.setBackground(
                 (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 4a467ce..d01fc93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -104,7 +104,7 @@
         // the active effect area. Values here should be kept in sync with the
         // animation implementation in the ripple shader.
         val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
-                (1 - rippleShader.progress)) * radius * 1.5f
+                (1 - rippleShader.progress)) * radius * 2
         canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 8770e86..796df55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -159,17 +159,6 @@
 
     @Override
     protected void onViewDetached() {
-        destroy();
-    }
-
-    @Override
-    public void destroy() {
-        // Don't receive future #onViewAttached calls so that we don't accidentally have two
-        // controllers registered for the same view.
-        // TODO(b/194181195): This shouldn't be necessary.
-        super.destroy();
-        mBatteryMeterViewController.destroy();
-
         mConfigurationController.removeCallback(mConfigurationListener);
         mAnimationScheduler.removeCallback(mAnimationCallback);
         mUserInfoController.removeCallback(mOnUserInfoChangedListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 50db240..ee242f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -928,10 +928,14 @@
             }
         }
 
+        mKeyguardStatusBarViewController =
+                mKeyguardStatusBarViewComponentFactory.build(mKeyguardStatusBar)
+                        .getKeyguardStatusBarViewController();
+        mKeyguardStatusBarViewController.init();
+
         updateViewControllers(
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarView,
-                mKeyguardStatusBar,
                 keyguardUserSwitcherView,
                 mCommunalView,
                 mView.findViewById(R.id.idle_host_view));
@@ -1025,7 +1029,6 @@
 
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             UserAvatarView userAvatarView,
-            KeyguardStatusBarView keyguardStatusBarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView,
             CommunalHostView communalView,
             IdleHostView idleHostView) {
@@ -1039,16 +1042,6 @@
         mIdleHostViewController = idleViewComponent.getIdleHostViewController();
         mIdleHostViewController.init();
 
-        KeyguardStatusBarViewComponent statusBarViewComponent =
-                mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView);
-        if (mKeyguardStatusBarViewController != null) {
-            // TODO(b/194181195): This shouldn't be necessary.
-            mKeyguardStatusBarViewController.destroy();
-        }
-        mKeyguardStatusBarViewController =
-                statusBarViewComponent.getKeyguardStatusBarViewController();
-        mKeyguardStatusBarViewController.init();
-
         if (communalView != null) {
             CommunalViewComponent communalViewComponent =
                     mCommunalViewComponentFactory.build(communalView);
@@ -1217,7 +1210,7 @@
 
         mBigClockContainer.removeAllViews();
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalView,
+                keyguardUserSwitcherView, mCommunalView,
                 mView.findViewById(R.id.idle_host_view));
 
         // Update keyguard bottom area
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index cfd82f5..377a7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -535,6 +535,10 @@
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
                     managedProfiles);
         }
+        onOverlaysApplied();
+    }
+
+    protected void onOverlaysApplied() {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 32bbe1c..0dd5788 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -110,16 +110,6 @@
     }
 
     /**
-     * Destroys this controller so that it never receives view attach and detach events again.
-     * Does nothing if the view is null.
-     */
-    public void destroy() {
-        if (mView != null) {
-            mView.removeOnAttachStateChangeListener(mOnAttachStateListener);
-        }
-    }
-
-    /**
      * Called when the view is attached and a call to {@link #init()} has been made in either order.
      */
     protected abstract void onViewAttached();
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index d5ea5bf..3a03573 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -1021,7 +1021,15 @@
                 }
             }
 
-            return upgradeAndInit(version, map);
+            try {
+                return upgradeAndInit(version, map);
+            } catch (Exception e) {
+                Log.wtf(TAG, "Failed to upgrade and set sensor privacy state,"
+                        + " resetting to default.", e);
+                mEnabled = new SparseBooleanArray();
+                mIndividualEnabled = new SparseArray<>();
+                return true;
+            }
         }
 
         private boolean upgradeAndInit(int version, SparseArray map) {
@@ -1037,22 +1045,22 @@
             final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
             if (version == 0) {
                 final boolean enabled = (boolean) map.get(VER0_ENABLED);
-                final SparseBooleanArray individualEnabled =
-                        (SparseBooleanArray) map.get(VER0_INDIVIDUAL_ENABLED);
+                final SparseArray<SensorState> individualEnabled =
+                        (SparseArray<SensorState>) map.get(VER0_INDIVIDUAL_ENABLED);
 
                 final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
-                final SparseArray<SparseBooleanArray> perUserIndividualEnabled =
+                final SparseArray<SparseArray<SensorState>> perUserIndividualEnabled =
                         new SparseArray<>();
 
                 // Copy global state to each user
                 for (int i = 0; i < users.length; i++) {
                     int user = users[i];
                     perUserEnabled.put(user, enabled);
-                    SparseBooleanArray userIndividualSensorEnabled = new SparseBooleanArray();
+                    SparseArray<SensorState> userIndividualSensorEnabled = new SparseArray<>();
                     perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
                     for (int j = 0; j < individualEnabled.size(); j++) {
                         final int sensor = individualEnabled.keyAt(j);
-                        final boolean isSensorEnabled = individualEnabled.valueAt(j);
+                        final SensorState isSensorEnabled = individualEnabled.valueAt(j);
                         userIndividualSensorEnabled.put(sensor, isSensorEnabled);
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2483fb8..317775f2 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -6567,10 +6567,17 @@
             final String msg = "Background started FGS: "
                     + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
                     + r.mInfoAllowStartForeground;
-            Slog.wtfQuiet(TAG, msg);
             if (r.mAllowStartForeground != REASON_DENIED) {
+                if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
+                        mAm.mConstants.mFgsStartAllowedLogSampleRate)) {
+                    Slog.wtfQuiet(TAG, msg);
+                }
                 Slog.i(TAG, msg);
             } else {
+                if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
+                        mAm.mConstants.mFgsStartDeniedLogSampleRate)) {
+                    Slog.wtfQuiet(TAG, msg);
+                }
                 Slog.w(TAG, msg);
             }
             r.mLoggedInfoAllowStartForeground = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index eb37e2d..9a755da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -112,6 +112,8 @@
     static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration";
     static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
     static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
+    static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate";
+    static final String KEY_FGS_START_DENIED_LOG_SAMPLE_RATE = "fgs_start_denied_log_sample_rate";
     static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out";
     static final String KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE =
             "extra_delay_svc_restart_mem_pressure";
@@ -160,6 +162,8 @@
     private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
     private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
     private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
+    private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25%
+    private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100%
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
      */
@@ -523,6 +527,20 @@
     volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE;
 
     /**
+     * Sample rate for the allowed FGS start WTF logs.
+     *
+     * If the value is 0.1, 10% of the logs would be sampled.
+     */
+    volatile float mFgsStartAllowedLogSampleRate = DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE;
+
+    /**
+     * Sample rate for the denied FGS start WTF logs.
+     *
+     * If the value is 0.1, 10% of the logs would be sampled.
+     */
+    volatile float mFgsStartDeniedLogSampleRate = DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE;
+
+    /**
      * Whether to allow "opt-out" from the foreground service restrictions.
      * (https://developer.android.com/about/versions/12/foreground-services)
      */
@@ -752,6 +770,12 @@
                             case KEY_FGS_ATOM_SAMPLE_RATE:
                                 updateFgsAtomSamplePercent();
                                 break;
+                            case KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE:
+                                updateFgsStartAllowedLogSamplePercent();
+                                break;
+                            case KEY_FGS_START_DENIED_LOG_SAMPLE_RATE:
+                                updateFgsStartDeniedLogSamplePercent();
+                                break;
                             case KEY_FGS_ALLOW_OPT_OUT:
                                 updateFgsAllowOptOut();
                                 break;
@@ -1104,6 +1128,20 @@
                 DEFAULT_FGS_ATOM_SAMPLE_RATE);
     }
 
+    private void updateFgsStartAllowedLogSamplePercent() {
+        mFgsStartAllowedLogSampleRate = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE,
+                DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE);
+    }
+
+    private void updateFgsStartDeniedLogSamplePercent() {
+        mFgsStartDeniedLogSampleRate = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FGS_START_DENIED_LOG_SAMPLE_RATE,
+                DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE);
+    }
+
     private void updateFgsAllowOptOut() {
         mFgsAllowOptOut = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1377,6 +1415,10 @@
         pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
         pw.print("  "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE);
         pw.print("="); pw.println(mFgsAtomSampleRate);
+        pw.print("  "); pw.print(KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE);
+        pw.print("="); pw.println(mFgsStartAllowedLogSampleRate);
+        pw.print("  "); pw.print(KEY_FGS_START_DENIED_LOG_SAMPLE_RATE);
+        pw.print("="); pw.println(mFgsStartDeniedLogSampleRate);
         pw.print("  "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
         pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
         pw.print("  "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
diff --git a/services/core/java/com/android/server/pm/PackageInstalledInfo.java b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
index afe6bb2..d0ca9d84 100644
--- a/services/core/java/com/android/server/pm/PackageInstalledInfo.java
+++ b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.pm.PackageManagerService.TAG;
 
-import android.content.pm.PackageParser;
 import android.util.ExceptionUtils;
 import android.util.Slog;
 
@@ -59,12 +58,6 @@
         Slog.w(TAG, msg);
     }
 
-    public void setError(String msg, PackageParser.PackageParserException e) {
-        setReturnCode(e.error);
-        setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
-        Slog.w(TAG, msg, e);
-    }
-
     public void setError(String msg, PackageManagerException e) {
         mReturnCode = e.error;
         setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fe419a6..b34a3a2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -261,6 +261,10 @@
                 new Lifecycle(context, this));
     }
 
+    StagingManager getStagingManager() {
+        return mStagingManager;
+    }
+
     boolean okToSendBroadcasts()  {
         return mOkToSendBroadcasts;
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2c15ab..94306ce 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -163,6 +163,7 @@
 import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IStagedApexObserver;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
@@ -202,6 +203,7 @@
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.SigningInfo;
+import android.content.pm.StagedApexInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.TestUtilityService;
 import android.content.pm.UserInfo;
@@ -23074,6 +23076,29 @@
         public boolean hasSystemFeature(String featureName, int version) {
             return PackageManagerService.this.hasSystemFeature(featureName, version);
         }
+
+        @Override
+        public void registerStagedApexObserver(IStagedApexObserver observer) {
+            mInstallerService.getStagingManager().registerStagedApexObserver(observer);
+        }
+
+        @Override
+        public void unregisterStagedApexObserver(IStagedApexObserver observer) {
+            mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
+        }
+
+        @Override
+        public String[] getStagedApexModuleNames() {
+            return mInstallerService.getStagingManager()
+                    .getStagedApexModuleNames().toArray(new String[0]);
+        }
+
+        @Override
+        @Nullable
+        public StagedApexInfo getStagedApexInfo(String moduleName) {
+            return mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+        }
+
     }
 
     private AndroidPackage getPackage(String packageName) {
@@ -25049,5 +25074,3 @@
         }
     }
 }
-
-
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9dcae3d..4dfb6b8 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.apex.ApexInfo;
 import android.apex.ApexInfoList;
 import android.apex.ApexSessionInfo;
@@ -28,6 +29,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.ApexStagedEvent;
+import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
@@ -36,6 +39,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.SigningDetails;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.StagedApexInfo;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
@@ -52,6 +56,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Slog;
@@ -80,7 +85,9 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -99,7 +106,8 @@
     private final ApexManager mApexManager;
     private final PowerManager mPowerManager;
     private final Context mContext;
-    private final PreRebootVerificationHandler mPreRebootVerificationHandler;
+    @VisibleForTesting
+    final PreRebootVerificationHandler mPreRebootVerificationHandler;
     private final Supplier<PackageParser2> mPackageParserSupplier;
 
     private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
@@ -115,6 +123,9 @@
     @GuardedBy("mSuccessfulStagedSessionIds")
     private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
 
+    @GuardedBy("mStagedApexObservers")
+    private final List<IStagedApexObserver> mStagedApexObservers = new ArrayList<>();
+
     private final CompletableFuture<Void> mBootCompleted = new CompletableFuture<>();
 
     interface StagedSession {
@@ -204,6 +215,35 @@
         mApexManager.markBootCompleted();
     }
 
+    void registerStagedApexObserver(IStagedApexObserver observer) {
+        if (observer == null) {
+            return;
+        }
+        if  (observer.asBinder() != null) {
+            try {
+                observer.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+                    @Override
+                    public void binderDied() {
+                        synchronized (mStagedApexObservers) {
+                            mStagedApexObservers.remove(observer);
+                        }
+                    }
+                }, 0);
+            } catch (RemoteException re) {
+                Slog.w(TAG, re.getMessage());
+            }
+        }
+        synchronized (mStagedApexObservers) {
+            mStagedApexObservers.add(observer);
+        }
+    }
+
+    void unregisterStagedApexObserver(IStagedApexObserver observer) {
+        synchronized (mStagedApexObservers) {
+            mStagedApexObservers.remove(observer);
+        }
+    }
+
     /**
      * Validates the signature used to sign the container of the new apex package
      *
@@ -808,6 +848,9 @@
                 // Also, cleaning up the stageDir prevents the apex from being activated.
                 Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
             }
+            if (session.containsApexSession()) {
+                notifyStagedApexObservers();
+            }
         }
 
         // Session was successfully aborted from apexd (if required) and pre-reboot verification
@@ -1093,7 +1136,107 @@
         return session;
     }
 
-    private final class PreRebootVerificationHandler extends Handler {
+    /**
+     * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>},
+     * where the key of the map is the module name of the ApexInfo.
+     *
+     * Returns an empty map if there is any error.
+     */
+    @VisibleForTesting
+    @NonNull
+    Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
+        Preconditions.checkArgument(session != null, "Session is null");
+        Preconditions.checkArgument(!session.hasParentSessionId(),
+                session.sessionId() + " session has parent session");
+        Preconditions.checkArgument(session.containsApexSession(),
+                session.sessionId() + " session does not contain apex");
+
+        // Even if caller calls this method on ready session, the session could be abandoned
+        // right after this method is called.
+        if (!session.isSessionReady() || session.isDestroyed()) {
+            return Collections.emptyMap();
+        }
+
+        ApexSessionParams params = new ApexSessionParams();
+        params.sessionId = session.sessionId();
+        final IntArray childSessionIds = new IntArray();
+        if (session.isMultiPackage()) {
+            for (StagedSession s : session.getChildSessions()) {
+                if (s.isApexSession()) {
+                    childSessionIds.add(s.sessionId());
+                }
+            }
+        }
+        params.childSessionIds = childSessionIds.toArray();
+
+        ApexInfo[] infos = mApexManager.getStagedApexInfos(params);
+        Map<String, ApexInfo> result = new ArrayMap<>();
+        for (ApexInfo info : infos) {
+            result.put(info.moduleName, info);
+        }
+        return result;
+    }
+
+    /**
+     * Returns apex module names of all packages that are staged ready
+     */
+    List<String> getStagedApexModuleNames() {
+        List<String> result = new ArrayList<>();
+        synchronized (mStagedSessions) {
+            for (int i = 0; i < mStagedSessions.size(); i++) {
+                final StagedSession session = mStagedSessions.valueAt(i);
+                if (!session.isSessionReady() || session.isDestroyed()
+                        || session.hasParentSessionId() || !session.containsApexSession()) {
+                    continue;
+                }
+                result.addAll(getStagedApexInfos(session).keySet());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null.
+     */
+    @Nullable
+    StagedApexInfo getStagedApexInfo(String moduleName) {
+        synchronized (mStagedSessions) {
+            for (int i = 0; i < mStagedSessions.size(); i++) {
+                final StagedSession session = mStagedSessions.valueAt(i);
+                if (!session.isSessionReady() || session.isDestroyed()
+                        || session.hasParentSessionId() || !session.containsApexSession()) {
+                    continue;
+                }
+                ApexInfo ai = getStagedApexInfos(session).get(moduleName);
+                if (ai != null) {
+                    StagedApexInfo info = new StagedApexInfo();
+                    info.moduleName = ai.moduleName;
+                    info.diskImagePath = ai.modulePath;
+                    info.versionCode = ai.versionCode;
+                    info.versionName = ai.versionName;
+                    return info;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void notifyStagedApexObservers() {
+        synchronized (mStagedApexObservers) {
+            for (IStagedApexObserver observer : mStagedApexObservers) {
+                ApexStagedEvent event = new ApexStagedEvent();
+                event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
+                try {
+                    observer.onApexStaged(event);
+                } catch (RemoteException re) {
+                    Slog.w(TAG, "Failed to contact the observer " + re.getMessage());
+                }
+            }
+        }
+    }
+
+    @VisibleForTesting
+    final class PreRebootVerificationHandler extends Handler {
 
         PreRebootVerificationHandler(Looper looper) {
             super(looper);
@@ -1114,7 +1257,8 @@
          */
         private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
         private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
-        private static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
+        @VisibleForTesting
+        static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1314,6 +1458,7 @@
                 if (hasApex) {
                     try {
                         mApexManager.markStagedSessionReady(session.sessionId());
+                        notifyStagedApexObservers();
                     } catch (PackageManagerException e) {
                         session.setSessionFailed(e.error, e.getMessage());
                         return;
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 8a8a302..4334bf6 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -21,7 +21,7 @@
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackage;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -82,7 +82,7 @@
         boolean hasClass = false;
         String className = "android.content.pm.AndroidTestBaseUpdater";
         try {
-            Class clazz = (PackageParser.class.getClassLoader().loadClass(className));
+            Class clazz = ParsingPackage.class.getClassLoader().loadClass(className);
             hasClass = clazz != null;
             Log.i(TAG, "Loaded " + className);
         } catch (ClassNotFoundException e) {
diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
index bdcd2cd..3524d7f 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
@@ -44,8 +44,8 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScreenState {}
 
-    private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
-    private Long mLastChanged = null;
+    private volatile @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
+    private volatile Long mLastChanged = null;
 
     private static final int LOG_SUBTYPE_UNFOLDED = 0;
     private static final int LOG_SUBTYPE_FOLDED = 1;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 75b66b3..2ac87a3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2798,9 +2798,11 @@
                         mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                         mPendingCapsLockToggle = false;
                     } else if (mPendingMetaAction) {
-                        launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
-                                event.getDeviceId(),
-                                event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
+                        if (!canceled) {
+                            launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
+                                    event.getDeviceId(),
+                                    event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
+                        }
                         mPendingMetaAction = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 2a95416..06253a0 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -124,12 +124,8 @@
         @Override
         public void read(InputStream in) throws IOException {
             while (in.available() > 0) {
-                try {
-                    DataElement dataElement = new DataElement(in);
-                    mCallback.onReadDataElement(dataElement.getData());
-                } catch (IOException e) {
-                    Slog.e(TAG, "Failed to read from storage. " + e.getMessage());
-                }
+                DataElement dataElement = new DataElement(in);
+                mCallback.onReadDataElement(dataElement.getData());
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3a5d771..ed2c3b6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8644,8 +8644,9 @@
             if (imeTargetWindowTask == null) {
                 return false;
             }
-            final TaskSnapshot snapshot = mAtmService.getTaskSnapshot(imeTargetWindowTask.mTaskId,
-                    false /* isLowResolution */);
+            final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+                    imeTargetWindowTask.mUserId, false /* isLowResolution */,
+                    false /* restoreFromDisk */);
             return snapshot != null && snapshot.hasImeSurface();
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 65b065a..620c287 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -945,7 +945,7 @@
                 final int displayId = r.getDisplayId();
                 final Context c = root.getDisplayUiContext(displayId);
 
-                if (r.mVisibleRequested && !displayContexts.contains(c)) {
+                if (c != null && r.mVisibleRequested && !displayContexts.contains(c)) {
                     displayContexts.add(c);
                 }
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6a25965..e38d8dc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3804,17 +3804,6 @@
         }
     }
 
-    @Override
-    public boolean isSeparateProfileChallengeAllowed(int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                String.format(NOT_SYSTEM_CALLER_MSG, "query separate challenge support"));
-
-        ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
-        // Profile challenge is supported on N or newer release.
-        return profileOwner != null &&
-                getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
-    }
-
     private boolean canSetPasswordQualityOnParent(String packageName, final CallerIdentity caller) {
         return !mInjector.isChangeEnabled(
                 PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, caller.getUserId())
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 21964dd..09a831e 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -51,7 +51,6 @@
         // classes generated by netd_aidl_interfaces-platform-java above.
         "netd_aidl_interface-V3-java",
         "networkstack-client",
-        "modules-utils-build_system",
     ],
     apex_available: [
         "com.android.wifi",
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml
new file mode 100644
index 0000000..b3c14228
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy enabled="false">
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 0fcda81..6a25560 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -678,6 +678,7 @@
 
     private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
         job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
+        job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
         job.setDeviceNotDozingConstraintSatisfied(
                 sElapsedRealtimeClock.millis(), isSatisfied, false);
         job.setBackgroundNotRestrictedConstraintSatisfied(
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index dd70c87..4738159 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -389,6 +389,8 @@
                 /* state */ true, /* allowlisted */false);
         js.setBackgroundNotRestrictedConstraintSatisfied(
                 sElapsedRealtimeClock.millis(), true, false);
+        js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+        js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
         return js;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index ba5a58f..b60e0c3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -30,17 +31,23 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
 
+import android.apex.ApexInfo;
 import android.apex.ApexSessionInfo;
+import android.apex.ApexSessionParams;
 import android.content.Context;
 import android.content.IntentSender;
+import android.content.pm.ApexStagedEvent;
+import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.StagedApexInfo;
+import android.os.Message;
 import android.os.SystemProperties;
 import android.os.storage.IStorageManager;
 import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
 import android.util.SparseArray;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -55,15 +62,20 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Predicate;
 
 @Presubmit
@@ -527,6 +539,281 @@
         assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
     }
 
+    @Test
+    public void getStagedApexInfos_validatePreConditions() throws Exception {
+        // Invalid session: null session
+        {
+            // Call and verify
+            IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+                    () -> mStagingManager.getStagedApexInfos(null));
+            assertThat(thrown).hasMessageThat().contains("Session is null");
+        }
+        // Invalid session: has parent
+        {
+            FakeStagedSession session = new FakeStagedSession(241);
+            session.setParentSessionId(239);
+            session.setSessionReady();
+            // Call and verify
+            IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+                    () -> mStagingManager.getStagedApexInfos(session));
+            assertThat(thrown).hasMessageThat().contains("241 session has parent");
+        }
+
+        // Invalid session: does not contain apex
+        {
+            FakeStagedSession session = new FakeStagedSession(241);
+            session.setSessionReady();
+            // Call and verify
+            IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+                    () -> mStagingManager.getStagedApexInfos(session));
+            assertThat(thrown).hasMessageThat().contains("241 session does not contain apex");
+        }
+        // Invalid session: not ready
+        {
+            FakeStagedSession session = new FakeStagedSession(239);
+            session.setIsApex(true);
+            // Call and verify
+            Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+            assertThat(result).isEmpty();
+        }
+        // Invalid session: destroyed
+        {
+            FakeStagedSession session = new FakeStagedSession(240);
+            session.setSessionReady();
+            session.setIsApex(true);
+            session.setDestroyed(true);
+            // Call and verify
+            Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+            assertThat(result).isEmpty();
+        }
+    }
+
+    private ApexInfo[] getFakeApexInfo(List<String> moduleNames) {
+        List<ApexInfo> result = new ArrayList<>();
+        for (String moduleName : moduleNames) {
+            ApexInfo info = new ApexInfo();
+            info.moduleName = moduleName;
+            result.add(info);
+        }
+        return result.toArray(new ApexInfo[0]);
+    }
+
+    @Test
+    public void getStagedApexInfos_nonParentSession() throws Exception {
+        FakeStagedSession validSession = new FakeStagedSession(239);
+        validSession.setIsApex(true);
+        validSession.setSessionReady();
+        ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+        when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+        // Call and verify
+        Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
+        assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]);
+
+        ArgumentCaptor<ApexSessionParams> argumentCaptor =
+                ArgumentCaptor.forClass(ApexSessionParams.class);
+        verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+        ApexSessionParams params = argumentCaptor.getValue();
+        assertThat(params.sessionId).isEqualTo(239);
+    }
+
+    @Test
+    public void getStagedApexInfos_parentSession() throws Exception {
+        FakeStagedSession childSession1 = new FakeStagedSession(201);
+        childSession1.setIsApex(true);
+        FakeStagedSession childSession2 = new FakeStagedSession(202);
+        childSession2.setIsApex(true);
+        FakeStagedSession nonApexChild = new FakeStagedSession(203);
+        FakeStagedSession parentSession = new FakeStagedSession(239,
+                Arrays.asList(childSession1, childSession2, nonApexChild));
+        parentSession.setSessionReady();
+        ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1", "module2"));
+        when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+        // Call and verify
+        Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
+        assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0],
+                fakeApexInfos[1].moduleName, fakeApexInfos[1]);
+
+        ArgumentCaptor<ApexSessionParams> argumentCaptor =
+                ArgumentCaptor.forClass(ApexSessionParams.class);
+        verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+        ApexSessionParams params = argumentCaptor.getValue();
+        assertThat(params.sessionId).isEqualTo(239);
+        assertThat(params.childSessionIds).asList().containsExactly(201, 202);
+    }
+
+    @Test
+    public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception {
+        FakeStagedSession validSession1 = new FakeStagedSession(239);
+        validSession1.setIsApex(true);
+        validSession1.setSessionReady();
+        mStagingManager.createSession(validSession1);
+
+        FakeStagedSession childSession1 = new FakeStagedSession(123);
+        childSession1.setIsApex(true);
+        FakeStagedSession childSession2 = new FakeStagedSession(124);
+        childSession2.setIsApex(true);
+        FakeStagedSession nonApexChild = new FakeStagedSession(125);
+        FakeStagedSession parentSession = new FakeStagedSession(240,
+                Arrays.asList(childSession1, childSession2, nonApexChild));
+        parentSession.setSessionReady();
+        mStagingManager.createSession(parentSession);
+
+        mockApexManagerGetStagedApexInfoWithSessionId();
+
+        List<String> result = mStagingManager.getStagedApexModuleNames();
+        assertThat(result).containsExactly("239", "123", "124");
+        verify(mApexManager, times(2)).getStagedApexInfos(any());
+    }
+
+    // Make mApexManager return ApexInfo with same module name as the sessionId
+    // of the parameter that was passed into it
+    private void mockApexManagerGetStagedApexInfoWithSessionId() {
+        when(mApexManager.getStagedApexInfos(any())).thenAnswer(new Answer<ApexInfo[]>() {
+            @Override
+            public ApexInfo[] answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ApexSessionParams params = (ApexSessionParams) args[0];
+                IntArray sessionsToProcess = new IntArray();
+                if (params.childSessionIds.length == 0) {
+                    sessionsToProcess.add(params.sessionId);
+                } else {
+                    sessionsToProcess.addAll(params.childSessionIds);
+                }
+                List<ApexInfo> result = new ArrayList<>();
+                for (int session : sessionsToProcess.toArray()) {
+                    ApexInfo info = new ApexInfo();
+                    info.moduleName = String.valueOf(session);
+                    result.add(info);
+                }
+                return result.toArray(new ApexInfo[0]);
+            }
+        });
+    }
+
+    @Test
+    public void getStagedApexInfo() throws Exception {
+        FakeStagedSession validSession1 = new FakeStagedSession(239);
+        validSession1.setIsApex(true);
+        validSession1.setSessionReady();
+        mStagingManager.createSession(validSession1);
+        ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+        when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+        // Verify null is returned if module name is not found
+        StagedApexInfo result = mStagingManager.getStagedApexInfo("not found");
+        assertThat(result).isNull();
+        verify(mApexManager, times(1)).getStagedApexInfos(any());
+        // Otherwise, the correct object is returned
+        result = mStagingManager.getStagedApexInfo("module1");
+        assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName);
+        assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath);
+        assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode);
+        assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName);
+        verify(mApexManager, times(2)).getStagedApexInfos(any());
+    }
+
+    @Test
+    public void registeredStagedApexObserverIsNotifiedOnPreRebootVerificationCompletion()
+            throws Exception {
+        // Register observer
+        IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+        mStagingManager.registerStagedApexObserver(observer);
+
+        // Create one staged session and trigger end of pre-reboot verification
+        {
+            FakeStagedSession session = new FakeStagedSession(239);
+            session.setIsApex(true);
+            mStagingManager.createSession(session);
+
+            mockApexManagerGetStagedApexInfoWithSessionId();
+            triggerEndOfPreRebootVerification(session);
+
+            assertThat(session.isSessionReady()).isTrue();
+            ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+                    ApexStagedEvent.class);
+            verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+            assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+                    new String[]{"239"});
+        }
+
+        // Create another staged session and verify observers are notified of union
+        {
+            Mockito.clearInvocations(observer);
+            FakeStagedSession session = new FakeStagedSession(240);
+            session.setIsApex(true);
+            mStagingManager.createSession(session);
+
+            triggerEndOfPreRebootVerification(session);
+
+            assertThat(session.isSessionReady()).isTrue();
+            ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+                    ApexStagedEvent.class);
+            verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+            assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+                    new String[]{"239", "240"});
+        }
+
+        // Finally, verify that once unregistered, observer is not notified
+        mStagingManager.unregisterStagedApexObserver(observer);
+        {
+            Mockito.clearInvocations(observer);
+            FakeStagedSession session = new FakeStagedSession(241);
+            session.setIsApex(true);
+            mStagingManager.createSession(session);
+
+            triggerEndOfPreRebootVerification(session);
+
+            assertThat(session.isSessionReady()).isTrue();
+            verify(observer, never()).onApexStaged(any());
+        }
+    }
+
+    @Test
+    public void registeredStagedApexObserverIsNotifiedOnSessionAbandon() throws Exception {
+        // Register observer
+        IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+        mStagingManager.registerStagedApexObserver(observer);
+
+        // Create a ready session and abandon it
+        FakeStagedSession session = new FakeStagedSession(239);
+        session.setIsApex(true);
+        session.setSessionReady();
+        session.setDestroyed(true);
+        mStagingManager.createSession(session);
+
+        mStagingManager.abortCommittedSession(session);
+
+        assertThat(session.isSessionReady()).isTrue();
+        ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+                ApexStagedEvent.class);
+        verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
+    }
+
+    @Test
+    public void stagedApexObserverIsOnlyCalledForApexSessions() throws Exception {
+        IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+        mStagingManager.registerStagedApexObserver(observer);
+
+        //  Trigger end of pre-reboot verification
+        FakeStagedSession session = new FakeStagedSession(239);
+        mStagingManager.createSession(session);
+
+        triggerEndOfPreRebootVerification(session);
+        assertThat(session.isSessionReady()).isTrue();
+        verify(observer, never()).onApexStaged(any());
+    }
+
+    private void triggerEndOfPreRebootVerification(StagingManager.StagedSession session) {
+        StagingManager.PreRebootVerificationHandler handler =
+                mStagingManager.mPreRebootVerificationHandler;
+        Message msg =  handler.obtainMessage(
+                handler.MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session);
+        handler.handleMessage(msg);
+    }
+
     private StagingManager.StagedSession createSession(int sessionId, String packageName,
             long committedMillis) {
         PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
@@ -591,10 +878,16 @@
         private String mPackageName;
         private boolean mIsAbandonded = false;
         private boolean mVerificationStarted = false;
-        private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
+        private final List<StagingManager.StagedSession> mChildSessions;
 
         private FakeStagedSession(int sessionId) {
             mSessionId = sessionId;
+            mChildSessions = new ArrayList<>();
+        }
+
+        private FakeStagedSession(int sessionId, List<StagingManager.StagedSession> childSessions) {
+            mSessionId = sessionId;
+            mChildSessions = childSessions;
         }
 
         private void setParentSessionId(int parentSessionId) {
@@ -777,9 +1070,7 @@
         }
 
         @Override
-        public void notifyEndPreRebootVerification() {
-            throw new UnsupportedOperationException();
-        }
+        public void notifyEndPreRebootVerification() {}
 
         @Override
         public void verifySession() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 844687f..ba79a76 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -60,6 +60,8 @@
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4);
     public static final String PERSISTENCE_FILE5 =
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5);
+    public static final String PERSISTENCE_FILE6 =
+            String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
 
     private Context mContext;
     @Mock
@@ -111,6 +113,7 @@
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
 
             // Try all files with two known users
             doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds();
@@ -124,6 +127,7 @@
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
 
         } finally {
             mockitoSession.finishMocking();
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 5a00e0d..8f9eb22 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -95,6 +95,8 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 
 /**
@@ -696,10 +698,12 @@
                         VibratorManagerService.OnSyncedVibrationCompleteListener.class);
         verify(mNativeWrapperMock).init(listenerCaptor.capture());
 
-        // Mock trigger callback on registered listener.
+        CountDownLatch triggerCountDown = new CountDownLatch(1);
+        // Mock trigger callback on registered listener right after the synced vibration starts.
         when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
         when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> {
             listenerCaptor.getValue().onComplete(answer.getArgument(0));
+            triggerCountDown.countDown();
             return true;
         });
 
@@ -708,20 +712,19 @@
                 .compose();
         CombinedVibration effect = CombinedVibration.createParallel(composed);
 
-        // Wait for vibration to start, it should finish right away with trigger callback.
         vibrate(service, effect, ALARM_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait until callback is triggered.
-        assertTrue(waitUntil(s -> !listenerCaptor.getAllValues().isEmpty(), service,
-                TEST_TIMEOUT_MILLIS));
+        // VibrationThread will start this vibration async, so wait until vibration is triggered.
+        triggerCountDown.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
 
         verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
         verify(mNativeWrapperMock).triggerSynced(anyLong());
-
         PrimitiveSegment expected = new PrimitiveSegment(
                 VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
         assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
         assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments());
+
+        // VibrationThread needs some time to react to native callbacks and stop the vibrator.
+        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
     }
 
     @Test
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceLogger.java b/services/usb/java/com/android/server/usb/UsbDeviceLogger.java
new file mode 100644
index 0000000..fab00bc
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbDeviceLogger.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb;
+
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.LinkedList;
+
+/**
+* Constructor UsbDeviceLogger class
+*/
+public class UsbDeviceLogger {
+    private final Object mLock = new Object();
+
+    // ring buffer of events to log.
+    @GuardedBy("mLock")
+    private final LinkedList<Event> mEvents;
+
+    private final String mTitle;
+
+    // the maximum number of events to keep in log
+    private final int mMemSize;
+
+    /**
+     * Constructor for Event class.
+     */
+    public abstract static class Event {
+        // formatter for timestamps
+        private static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+        private final Calendar mCalendar;
+
+        Event() {
+            mCalendar = Calendar.getInstance();
+        }
+
+    /**
+     * Convert event to String
+     * @return StringBuilder
+     */
+        public String toString() {
+            return (new StringBuilder(String.format("%tm-%td %tH:%tM:%tS.%tL",
+                    mCalendar, mCalendar, mCalendar, mCalendar, mCalendar, mCalendar)))
+                    .append(" ").append(eventToString()).toString();
+        }
+
+        /**
+         * Causes the string message for the event to appear in the logcat.
+         * Here is an example of how to create a new event (a StringEvent), adding it to the logger
+         * (an instance of UsbDeviceLoggerr) while also making it show in the logcat:
+         * <pre>
+         *     myLogger.log(
+         *         (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
+         * </pre>
+         * @param tag the tag for the android.util.Log.v
+         * @return the same instance of the event
+         */
+        public Event printLog(String tag) {
+            Log.i(tag, eventToString());
+            return this;
+        }
+
+        /**
+         * Convert event to String.
+         * This method is only called when the logger history is about to the dumped,
+         * so this method is where expensive String conversions should be made, not when the Event
+         * subclass is created.
+         * Timestamp information will be automatically added, do not include it.
+         * @return a string representation of the event that occurred.
+         */
+        public abstract String eventToString();
+    }
+
+    /**
+    * Constructor StringEvent class
+    */
+    public static class StringEvent extends Event {
+        private final String mMsg;
+
+        public StringEvent(String msg) {
+            mMsg = msg;
+        }
+
+        @Override
+        public String eventToString() {
+            return mMsg;
+        }
+    }
+
+    /**
+     * Constructor for logger.
+     * @param size the maximum number of events to keep in log
+     * @param title the string displayed before the recorded log
+     */
+    public UsbDeviceLogger(int size, String title) {
+        mEvents = new LinkedList<Event>();
+        mMemSize = size;
+        mTitle = title;
+    }
+
+    /**
+     * Constructor for logger.
+     * @param evt the maximum number of events to keep in log
+     */
+    public synchronized void log(Event evt) {
+        synchronized (mLock) {
+            if (mEvents.size() >= mMemSize) {
+                mEvents.removeFirst();
+            }
+            mEvents.add(evt);
+        }
+    }
+
+    /**
+     * Constructor for logger.
+     * @param dump the maximum number of events to keep in log
+     * @param id the category of events
+     */
+    public synchronized void dump(DualDumpOutputStream dump, long id) {
+        dump.write("USB Event Log", id, mTitle);
+        for (Event evt : mEvents) {
+            dump.write("USB Event", id, evt.toString());
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index b966643..27aabb5 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -191,6 +191,8 @@
      */
     private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
 
+    private static final int DUMPSYS_LOG_BUFFER = 200;
+
     private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
 
     private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -210,6 +212,8 @@
     private static Set<Integer> sDenyInterfaces;
     private HashMap<Long, FileDescriptor> mControlFds;
 
+    private static UsbDeviceLogger sEventLogger;
+
     static {
         sDenyInterfaces = new HashSet<>();
         sDenyInterfaces.add(UsbConstants.USB_CLASS_AUDIO);
@@ -232,9 +236,11 @@
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
             if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
+            sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString()));
 
             String state = event.get("USB_STATE");
             String accessory = event.get("ACCESSORY");
+
             if (state != null) {
                 mHandler.updateState(state);
             } else if ("GETPROTOCOL".equals(accessory)) {
@@ -382,6 +388,8 @@
         mUEventObserver = new UsbUEventObserver();
         mUEventObserver.startObserving(USB_STATE_MATCH);
         mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+        sEventLogger = new UsbDeviceLogger(DUMPSYS_LOG_BUFFER, "UsbDeviceManager activity");
     }
 
     UsbProfileGroupSettingsManager getCurrentSettings() {
@@ -814,6 +822,7 @@
 
         protected void sendStickyBroadcast(Intent intent) {
             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            sEventLogger.log(new UsbDeviceLogger.StringEvent("USB intent: " + intent));
         }
 
         private void updateUsbFunctions() {
@@ -2285,6 +2294,7 @@
 
         if (mHandler != null) {
             mHandler.dump(dump, "handler", UsbDeviceManagerProto.HANDLER);
+            sEventLogger.dump(dump, UsbHandlerProto.UEVENT);
         }
 
         dump.end(token);
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index f638660..bb0c4e9 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -60,7 +60,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.Immutable;
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
 
@@ -75,7 +74,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.net.ProtocolException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -86,6 +84,8 @@
     private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    private static final int DUMPSYS_LOG_BUFFER = 200;
+
     /** Legacy settings file, before multi-user */
     private static final File sSingleUserSettingsFile = new File(
             "/data/system/usb_device_manager.xml");
@@ -130,6 +130,8 @@
     @GuardedBy("mLock")
     private boolean mIsWriteSettingsScheduled;
 
+    private static UsbDeviceLogger sEventLogger;
+
     /**
      * A package of a user.
      */
@@ -260,6 +262,9 @@
                         device, false /* showMtpNotification */));
 
         mUsbHandlerManager = usbResolveActivityManager;
+
+        sEventLogger = new UsbDeviceLogger(DUMPSYS_LOG_BUFFER,
+                "UsbProfileGroupSettingsManager activity");
     }
 
     /**
@@ -965,6 +970,7 @@
                     matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
         }
 
+        sEventLogger.log(new UsbDeviceLogger.StringEvent("accessoryAttached: " + intent));
         resolveActivity(intent, matches, defaultActivity, null, accessory);
     }
 
@@ -1518,6 +1524,7 @@
             }
         }
 
+        sEventLogger.dump(dump, UsbProfileGroupSettingsManagerProto.INTENT);
         dump.end(token);
     }
 
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index cac14a7..558798d 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -37,6 +37,7 @@
         ":test.rebootless_apex_v1",
         ":test.rebootless_apex_v2",
     ],
+    platform_apis: true,
 }
 
 java_test_host {
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 4684f01..c610641 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -17,16 +17,27 @@
 package com.android.tests.stagedinstallinternal;
 
 import static com.android.cts.install.lib.InstallUtils.getPackageInstaller;
+import static com.android.cts.install.lib.InstallUtils.waitForSessionReady;
 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
 import android.Manifest;
+import android.content.pm.ApexStagedEvent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
+import android.content.pm.StagedApexInfo;
+import android.os.IBinder;
+import android.os.ServiceManager;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -39,6 +50,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -401,9 +414,73 @@
                 AssertionError.class,
                 "Staged session " + sessionId + " already contains " + SHIM_APEX_PACKAGE_NAME,
                 Install.single(APEX_V2));
-
     }
 
+    @Test
+    public void testGetStagedModuleNames() throws Exception {
+        // Before staging a session
+        String[] result = getPackageManagerNative().getStagedApexModuleNames();
+        assertThat(result).hasLength(0);
+        // Stage an apex
+        int sessionId = Install.single(APEX_V2).setStaged().commit();
+        waitForSessionReady(sessionId);
+        result = getPackageManagerNative().getStagedApexModuleNames();
+        assertThat(result).hasLength(1);
+        assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME});
+        // Abandon the session
+        InstallUtils.openPackageInstallerSession(sessionId).abandon();
+        result = getPackageManagerNative().getStagedApexModuleNames();
+        assertThat(result).hasLength(0);
+    }
+
+    @Test
+    public void testGetStagedApexInfo() throws Exception {
+        // Ask for non-existing module
+        StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
+        assertThat(result).isNull();
+        // Stage an apex
+        int sessionId = Install.single(APEX_V2).setStaged().commit();
+        waitForSessionReady(sessionId);
+        // Query proper module name
+        result = getPackageManagerNative().getStagedApexInfo(SHIM_APEX_PACKAGE_NAME);
+        assertThat(result.moduleName).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+        InstallUtils.openPackageInstallerSession(sessionId).abandon();
+    }
+
+    public static class MockStagedApexObserver extends IStagedApexObserver.Stub {
+        @Override
+        public void onApexStaged(ApexStagedEvent event) {
+            assertThat(event).isNotNull();
+        }
+    }
+
+    @Test
+    public void testStagedApexObserver() throws Exception {
+        MockStagedApexObserver realObserver = new MockStagedApexObserver();
+        IStagedApexObserver observer = spy(realObserver);
+        assertThat(observer).isNotNull();
+        getPackageManagerNative().registerStagedApexObserver(observer);
+
+        // Stage an apex and verify observer was called
+        int sessionId = Install.single(APEX_V2).setStaged().commit();
+        waitForSessionReady(sessionId);
+        ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class);
+        verify(observer, timeout(5000)).onApexStaged(captor.capture());
+        assertThat(captor.getValue().stagedApexModuleNames).isEqualTo(
+                new String[] {SHIM_APEX_PACKAGE_NAME});
+
+        // Abandon and verify observer is called
+        Mockito.clearInvocations(observer);
+        InstallUtils.openPackageInstallerSession(sessionId).abandon();
+        verify(observer, timeout(5000)).onApexStaged(captor.capture());
+        assertThat(captor.getValue().stagedApexModuleNames).hasLength(0);
+    }
+
+    private IPackageManagerNative getPackageManagerNative() {
+        IBinder binder = ServiceManager.waitForService("package_native");
+        assertThat(binder).isNotNull();
+        return IPackageManagerNative.Stub.asInterface(binder);
+    }
     private static void assertSessionApplied(int sessionId) {
         assertSessionState(sessionId, (session) -> {
             assertThat(session.isStagedSessionApplied()).isTrue();
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 5021009..3102103 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -478,6 +478,21 @@
         runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails");
     }
 
+    @Test
+    public void testGetStagedModuleNames() throws Exception {
+        runPhase("testGetStagedModuleNames");
+    }
+
+    @Test
+    public void testGetStagedApexInfo() throws Exception {
+        runPhase("testGetStagedApexInfo");
+    }
+
+    @Test
+    public void testStagedApexObserver() throws Exception {
+        runPhase("testStagedApexObserver");
+    }
+
     private List<String> getStagingDirectories() throws DeviceNotAvailableException {
         String baseDir = "/data/app-staging";
         try {