Merge "Open dialog panel after long pressing a direct share target to allow pin/unpin the target. Communicate with ShortcutManager/Launcherapps to store pin info in ShortcutService." into tm-dev
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 38500af..f6ae56f 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -507,6 +507,22 @@
     }
 
     /**
+     * Release all the leases which are currently held by the caller.
+     *
+     * @hide
+     */
+    public void releaseAllLeases() throws Exception {
+        try {
+            mService.releaseAllLeases(mContext.getOpPackageName());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the remaining quota size for acquiring a lease (in bytes) which indicates the
      * remaining amount of data that an app can acquire a lease on before the System starts
      * rejecting lease requests.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index 39a9fb4..1566fa8 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -31,6 +31,7 @@
     void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
             long leaseTimeoutMillis, in String packageName);
     void releaseLease(in BlobHandle handle, in String packageName);
+    void releaseAllLeases(in String packageName);
     long getRemainingLeaseQuotaBytes(String packageName);
 
     void waitForIdle(in RemoteCallback callback);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index c83ca8c..9ac3e41 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -554,6 +554,21 @@
         }
     }
 
+    private void releaseAllLeasesInternal(int callingUid, String callingPackage) {
+        synchronized (mBlobsLock) {
+            // Remove the package from the leasee list
+            mBlobsMap.forEach((blobHandle, blobMetadata) -> {
+                blobMetadata.removeLeasee(callingPackage, callingUid);
+            });
+            writeBlobsInfoAsync();
+
+            if (LOGV) {
+                Slog.v(TAG, "Release all leases associated with pkg="
+                        + callingPackage + ", uid=" + callingUid);
+            }
+        }
+    }
+
     private long getRemainingLeaseQuotaBytesInternal(int callingUid, String callingPackage) {
         synchronized (mBlobsLock) {
             final long remainingQuota = BlobStoreConfig.getAppDataBytesLimit()
@@ -1376,7 +1391,7 @@
         }
     }
 
-    private boolean isAllowedBlobAccess(int uid, String packageName) {
+    private boolean isAllowedBlobStoreAccess(int uid, String packageName) {
         return (!Process.isSdkSandboxUid(uid) && !Process.isIsolated(uid)
                 && !mPackageManagerInternal.isInstantApp(packageName, UserHandle.getUserId(uid)));
     }
@@ -1442,7 +1457,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (!isAllowedBlobAccess(callingUid, packageName)) {
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to create session; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1491,7 +1506,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (!isAllowedBlobAccess(callingUid, packageName)) {
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1522,7 +1537,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (!isAllowedBlobAccess(callingUid, packageName)) {
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1546,7 +1561,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (!isAllowedBlobAccess(callingUid, packageName)) {
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1555,6 +1570,21 @@
         }
 
         @Override
+        public void releaseAllLeases(@NonNull String packageName) {
+            Objects.requireNonNull(packageName, "packageName must not be null");
+
+            final int callingUid = Binder.getCallingUid();
+            verifyCallingPackage(callingUid, packageName);
+
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
+                throw new SecurityException("Caller not allowed to open blob; "
+                        + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+            }
+
+            releaseAllLeasesInternal(callingUid, packageName);
+        }
+
+        @Override
         public long getRemainingLeaseQuotaBytes(@NonNull String packageName) {
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -1629,7 +1659,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (!isAllowedBlobAccess(callingUid, packageName)) {
+            if (!isAllowedBlobStoreAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
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 c0a8148..f4faec8 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -28,8 +28,8 @@
 import static com.android.server.tare.EconomicPolicy.eventToString;
 import static com.android.server.tare.EconomicPolicy.getEventType;
 import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.cakeToString;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -161,7 +161,7 @@
 
     @GuardedBy("mLock")
     private boolean isAffordableLocked(long balance, long price, long ctp) {
-        return balance >= price && mScribe.getRemainingConsumableNarcsLocked() >= ctp;
+        return balance >= price && mScribe.getRemainingConsumableCakesLocked() >= ctp;
     }
 
     @GuardedBy("mLock")
@@ -464,13 +464,13 @@
                     + eventToString(transaction.eventId)
                     + (transaction.tag == null ? "" : ":" + transaction.tag)
                     + " for " + appToString(userId, pkgName)
-                    + " by " + narcToString(transaction.delta - newDelta));
+                    + " by " + cakeToString(transaction.delta - newDelta));
             transaction = new Ledger.Transaction(
                     transaction.startTimeMs, transaction.endTimeMs,
                     transaction.eventId, transaction.tag, newDelta, transaction.ctp);
         }
         ledger.recordTransaction(transaction);
-        mScribe.adjustRemainingConsumableNarcsLocked(-transaction.ctp);
+        mScribe.adjustRemainingConsumableCakesLocked(-transaction.ctp);
         if (transaction.delta != 0 && notifyOnAffordabilityChange) {
             final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
                     mActionAffordabilityNotes.get(userId, pkgName);
@@ -724,7 +724,7 @@
     private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
         final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
         if (ledger.getCurrentBalance() != 0) {
-            mScribe.adjustRemainingConsumableNarcsLocked(-ledger.getCurrentBalance());
+            mScribe.adjustRemainingConsumableCakesLocked(-ledger.getCurrentBalance());
         }
         mScribe.discardLedgerLocked(userId, pkgName);
         mCurrentOngoingEvents.delete(userId, pkgName);
@@ -872,7 +872,7 @@
             return;
         }
         mTrendCalculator.reset(getBalanceLocked(userId, pkgName),
-                mScribe.getRemainingConsumableNarcsLocked(),
+                mScribe.getRemainingConsumableCakesLocked(),
                 mActionAffordabilityNotes.get(userId, pkgName));
         ongoingEvents.forEach(mTrendCalculator);
         final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
@@ -1260,11 +1260,11 @@
                         pw.print(" runtime=");
                         TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
                         pw.print(" delta/sec=");
-                        pw.print(narcToString(ongoingEvent.getDeltaPerSec()));
+                        pw.print(cakeToString(ongoingEvent.getDeltaPerSec()));
                         final long ctp = ongoingEvent.getCtpPerSec();
                         if (ctp != 0) {
                             pw.print(" ctp/sec=");
-                            pw.print(narcToString(ongoingEvent.getCtpPerSec()));
+                            pw.print(cakeToString(ongoingEvent.getCtpPerSec()));
                         }
                         pw.print(" refCount=");
                         pw.print(ongoingEvent.refCount);
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 71e00cf..c2e8188 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -97,8 +97,8 @@
 import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
-import static com.android.server.tare.TareUtils.arcToNarc;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.arcToCake;
+import static com.android.server.tare.TareUtils.cakeToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -219,143 +219,143 @@
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceExempted = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
+        mMinSatiatedBalanceExempted = arcToCake(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
                 DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED));
-        mMinSatiatedBalanceOther = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
+        mMinSatiatedBalanceOther = arcToCake(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,
+        mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
                 DEFAULT_AM_MAX_SATIATED_BALANCE));
-        mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+        mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt(
                 KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT));
         mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
-                arcToNarc(mParser.getInt(
+                arcToCake(mParser.getInt(
                         KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT)));
 
-        final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
+        final long exactAllowWhileIdleWakeupBasePrice = arcToCake(
                 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(mParser.getInt(
+                        arcToCake(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(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
+                        arcToCake(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,
+                        arcToCake(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE))));
 
         final long inexactAllowWhileIdleWakeupBasePrice =
-                arcToNarc(mParser.getInt(
+                arcToCake(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(mParser.getInt(
+                        arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE))));
 
         final long exactAllowWhileIdleNonWakeupBasePrice =
-                arcToNarc(mParser.getInt(
+                arcToCake(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(mParser.getInt(
+                        arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE))));
 
         final long inexactAllowWhileIdleNonWakeupBasePrice =
-                arcToNarc(mParser.getInt(
+                arcToCake(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(mParser.getInt(
+                        arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
                                 DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
                                 DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
                                 DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE))));
 
         mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
+                arcToCake(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,
+                (long) (arcToCake(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
                         DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX,
+                arcToCake(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,
+                arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
                         DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT)),
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
+                arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
                         DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
+                arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
                                 DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
                                 DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(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,
+                arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
                         DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT)),
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
+                arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
                         DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
+                arcToCake(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(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
+                        arcToCake(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
                                 DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX))));
     }
@@ -364,14 +364,14 @@
     void dump(IndentingPrintWriter pw) {
         pw.println("Min satiated balances:");
         pw.increaseIndent();
-        pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
-        pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+        pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
+        pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
         pw.decreaseIndent();
-        pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
+        pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
         pw.print("Consumption limits: [");
-        pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+        pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
         pw.print(", ");
-        pw.print(narcToString(mHardSatiatedConsumptionLimit));
+        pw.print(cakeToString(mHardSatiatedConsumptionLimit));
         pw.println("]");
 
         pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index 1e48015..3a26aae 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -21,7 +21,7 @@
 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
 import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.cakeToString;
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
@@ -203,7 +203,7 @@
     abstract long getMaxSatiatedBalance();
 
     /**
-     * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+     * Returns the maximum number of cakes that should be consumed during a full 100% discharge
      * cycle. This is the initial limit. The system may choose to increase the limit over time,
      * but the increased limit should never exceed the value returned from
      * {@link #getHardSatiatedConsumptionLimit()}.
@@ -211,7 +211,7 @@
     abstract long getInitialSatiatedConsumptionLimit();
 
     /**
-     * Returns the maximum number of narcs that should be consumed during a full 100% discharge
+     * Returns the maximum number of cakes that should be consumed during a full 100% discharge
      * cycle. This is the hard limit that should never be exceeded.
      */
     abstract long getHardSatiatedConsumptionLimit();
@@ -430,9 +430,9 @@
         pw.print(actionToString(action.id));
         pw.print(": ");
         pw.print("ctp=");
-        pw.print(narcToString(action.costToProduce));
+        pw.print(cakeToString(action.costToProduce));
         pw.print(", basePrice=");
-        pw.print(narcToString(action.basePrice));
+        pw.print(cakeToString(action.basePrice));
         pw.println();
     }
 
@@ -440,11 +440,11 @@
         pw.print(rewardToString(reward.id));
         pw.print(": ");
         pw.print("instant=");
-        pw.print(narcToString(reward.instantReward));
+        pw.print(cakeToString(reward.instantReward));
         pw.print(", ongoing/sec=");
-        pw.print(narcToString(reward.ongoingRewardPerSecond));
+        pw.print(cakeToString(reward.ongoingRewardPerSecond));
         pw.print(", maxDaily=");
-        pw.print(narcToString(reward.maxDailyReward));
+        pw.print(cakeToString(reward.maxDailyReward));
         pw.println();
     }
 }
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 c934807..ce4604f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -23,8 +23,8 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 
 import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.cakeToString;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -528,9 +528,9 @@
     void maybePerformQuantitativeEasingLocked() {
         // We don't need to increase the limit if the device runs out of consumable credits
         // when the battery is low.
-        final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
+        final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
         if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD
-                || remainingConsumableNarcs > 0) {
+                || remainingConsumableCakes > 0) {
             return;
         }
         final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked();
@@ -539,8 +539,8 @@
         final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall,
                 mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit());
         if (newConsumptionLimit != currentConsumptionLimit) {
-            Slog.i(TAG, "Increasing consumption limit from " + narcToString(currentConsumptionLimit)
-                    + " to " + narcToString(newConsumptionLimit));
+            Slog.i(TAG, "Increasing consumption limit from " + cakeToString(currentConsumptionLimit)
+                    + " to " + cakeToString(newConsumptionLimit));
             mScribe.setConsumptionLimitLocked(newConsumptionLimit);
             adjustCreditSupplyLocked(/* allowIncrease */ true);
         }
@@ -562,16 +562,16 @@
     @GuardedBy("mLock")
     private void adjustCreditSupplyLocked(boolean allowIncrease) {
         final long newLimit = getConsumptionLimitLocked();
-        final long remainingConsumableNarcs = mScribe.getRemainingConsumableNarcsLocked();
-        if (remainingConsumableNarcs == newLimit) {
+        final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked();
+        if (remainingConsumableCakes == newLimit) {
             return;
         }
-        if (remainingConsumableNarcs > newLimit) {
-            mScribe.adjustRemainingConsumableNarcsLocked(newLimit - remainingConsumableNarcs);
+        if (remainingConsumableCakes > newLimit) {
+            mScribe.adjustRemainingConsumableCakesLocked(newLimit - remainingConsumableCakes);
         } else if (allowIncrease) {
             final double perc = mCurrentBatteryLevel / 100d;
-            final long shortfall = newLimit - remainingConsumableNarcs;
-            mScribe.adjustRemainingConsumableNarcsLocked((long) (perc * shortfall));
+            final long shortfall = newLimit - remainingConsumableCakes;
+            mScribe.adjustRemainingConsumableCakesLocked((long) (perc * shortfall));
         }
         mAgent.onCreditSupplyChanged();
     }
@@ -919,7 +919,7 @@
                             + cost.price * (action.ongoingDurationMs / 1000);
                 }
                 return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance
-                        && mScribe.getRemainingConsumableNarcsLocked() >= requiredBalance;
+                        && mScribe.getRemainingConsumableCakesLocked() >= requiredBalance;
             }
         }
 
@@ -947,7 +947,7 @@
                 }
                 final long minBalance = Math.min(
                         mAgent.getBalanceLocked(userId, pkgName),
-                        mScribe.getRemainingConsumableNarcsLocked());
+                        mScribe.getRemainingConsumableCakesLocked());
                 return minBalance * 1000 / totalCostPerSecond;
             }
         }
@@ -1103,21 +1103,21 @@
 
             final long consumptionLimit = getConsumptionLimitLocked();
             pw.print("Consumption limit (current/initial-satiated/current-satiated): ");
-            pw.print(narcToString(consumptionLimit));
+            pw.print(cakeToString(consumptionLimit));
             pw.print("/");
-            pw.print(narcToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
+            pw.print(cakeToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()));
             pw.print("/");
-            pw.println(narcToString(mScribe.getSatiatedConsumptionLimitLocked()));
+            pw.println(cakeToString(mScribe.getSatiatedConsumptionLimitLocked()));
 
-            final long remainingConsumable = mScribe.getRemainingConsumableNarcsLocked();
+            final long remainingConsumable = mScribe.getRemainingConsumableCakesLocked();
             pw.print("Goods remaining: ");
-            pw.print(narcToString(remainingConsumable));
+            pw.print(cakeToString(remainingConsumable));
             pw.print(" (");
             pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit));
             pw.println("% of current limit)");
 
             pw.print("Device wealth: ");
-            pw.println(narcToString(mScribe.getNarcsInCirculationForLoggingLocked()));
+            pw.println(cakeToString(mScribe.getCakesInCirculationForLoggingLocked()));
 
             pw.println();
             pw.print("Exempted apps", mExemptedApps);
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 0eddd22..99b93ce 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -106,8 +106,8 @@
 import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
 import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
-import static com.android.server.tare.TareUtils.arcToNarc;
-import static com.android.server.tare.TareUtils.narcToString;
+import static com.android.server.tare.TareUtils.arcToCake;
+import static com.android.server.tare.TareUtils.cakeToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -221,116 +221,116 @@
             Slog.e(TAG, "Global setting key incorrect: ", e);
         }
 
-        mMinSatiatedBalanceExempted = arcToNarc(
+        mMinSatiatedBalanceExempted = arcToCake(
                 mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
                         DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED));
-        mMinSatiatedBalanceOther = arcToNarc(
+        mMinSatiatedBalanceOther = arcToCake(
                 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,
+        mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
                 DEFAULT_JS_MAX_SATIATED_BALANCE));
-        mInitialSatiatedConsumptionLimit = arcToNarc(mParser.getInt(
+        mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt(
                 KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT));
         mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit,
-                arcToNarc(mParser.getInt(
+                arcToCake(mParser.getInt(
                         KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT)));
 
         mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
-                arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                arcToCake(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,
+                (long) (arcToCake(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
                         DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX,
+                arcToCake(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,
+                arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT)),
-                arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
+                arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
                         DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
+                arcToCake(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(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
                                 DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(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,
+                arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT)),
-                arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
+                arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
                         DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING)),
-                arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
+                arcToCake(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(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
+                        arcToCake(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING)),
-                        arcToNarc(mParser.getInt(
+                        arcToCake(mParser.getInt(
                                 KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
                                 DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX))));
     }
@@ -339,14 +339,14 @@
     void dump(IndentingPrintWriter pw) {
         pw.println("Min satiated balances:");
         pw.increaseIndent();
-        pw.print("Exempted", narcToString(mMinSatiatedBalanceExempted)).println();
-        pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
+        pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
+        pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
         pw.decreaseIndent();
-        pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
+        pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
         pw.print("Consumption limits: [");
-        pw.print(narcToString(mInitialSatiatedConsumptionLimit));
+        pw.print(cakeToString(mInitialSatiatedConsumptionLimit));
         pw.print(", ");
-        pw.print(narcToString(mHardSatiatedConsumptionLimit));
+        pw.print(cakeToString(mHardSatiatedConsumptionLimit));
         pw.println("]");
 
         pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index dfdc20a..2e2a9b5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -18,9 +18,9 @@
 
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 
+import static com.android.server.tare.TareUtils.cakeToString;
 import static com.android.server.tare.TareUtils.dumpTime;
 import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
-import static com.android.server.tare.TareUtils.narcToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -129,7 +129,7 @@
     }
 
     void dump(IndentingPrintWriter pw, int numRecentTransactions) {
-        pw.print("Current balance", narcToString(getCurrentBalance())).println();
+        pw.print("Current balance", cakeToString(getCurrentBalance())).println();
 
         final int size = mTransactions.size();
         for (int i = Math.max(0, size - numRecentTransactions); i < size; ++i) {
@@ -146,9 +146,9 @@
                 pw.print(")");
             }
             pw.print(" --> ");
-            pw.print(narcToString(transaction.delta));
+            pw.print(cakeToString(transaction.delta));
             pw.print(" (ctp=");
-            pw.print(narcToString(transaction.ctp));
+            pw.print(cakeToString(transaction.ctp));
             pw.println(")");
         }
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md
index 33eadff..72d5069 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/README.md
+++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md
@@ -105,5 +105,7 @@
 
 * ARC: Android Resource Credits are the "currency" units used as an abstraction layer over the real
   battery drain. They allow the system to standardize costs and prices across various devices.
+* Cake: A lie; also the smallest unit of an ARC (1 cake = one-billionth of an ARC = 1 nano-ARC).
+  When the apps request to do something, we shall let them eat cake.
 * NARC: The smallest unit of an ARC. A narc is 1 nano-ARC.
 * Satiated: used to refer to when the device is fully charged (at 100% battery level)
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 8662110..7442877 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -84,7 +84,7 @@
     private static final String XML_ATTR_USER_ID = "userId";
     private static final String XML_ATTR_VERSION = "version";
     private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime";
-    private static final String XML_ATTR_REMAINING_CONSUMABLE_NARCS = "remainingConsumableNarcs";
+    private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes";
     private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit";
 
     /** Version of the file schema. */
@@ -100,7 +100,7 @@
     @GuardedBy("mIrs.getLock()")
     private long mSatiatedConsumptionLimit;
     @GuardedBy("mIrs.getLock()")
-    private long mRemainingConsumableNarcs;
+    private long mRemainingConsumableCakes;
     @GuardedBy("mIrs.getLock()")
     private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
 
@@ -122,10 +122,10 @@
     }
 
     @GuardedBy("mIrs.getLock()")
-    void adjustRemainingConsumableNarcsLocked(long delta) {
+    void adjustRemainingConsumableCakesLocked(long delta) {
         if (delta != 0) {
             // No point doing any work if the change is 0.
-            mRemainingConsumableNarcs += delta;
+            mRemainingConsumableCakes += delta;
             postWrite();
         }
     }
@@ -168,7 +168,7 @@
      * call it for normal operation.
      */
     @GuardedBy("mIrs.getLock()")
-    long getNarcsInCirculationForLoggingLocked() {
+    long getCakesInCirculationForLoggingLocked() {
         long sum = 0;
         for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
             for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) {
@@ -178,10 +178,10 @@
         return sum;
     }
 
-    /** Returns the total amount of narcs that remain to be consumed. */
+    /** Returns the total amount of cakes that remain to be consumed. */
     @GuardedBy("mIrs.getLock()")
-    long getRemainingConsumableNarcsLocked() {
-        return mRemainingConsumableNarcs;
+    long getRemainingConsumableCakesLocked() {
+        return mRemainingConsumableCakes;
     }
 
     @GuardedBy("mIrs.getLock()")
@@ -189,11 +189,11 @@
         mLedgers.clear();
         if (!recordExists()) {
             mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked();
-            mRemainingConsumableNarcs = mIrs.getConsumptionLimitLocked();
+            mRemainingConsumableCakes = mIrs.getConsumptionLimitLocked();
             return;
         }
         mSatiatedConsumptionLimit = 0;
-        mRemainingConsumableNarcs = 0;
+        mRemainingConsumableCakes = 0;
 
         final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
         final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
@@ -254,8 +254,8 @@
                                 parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT,
                                         mIrs.getInitialSatiatedConsumptionLimitLocked());
                         final long consumptionLimit = mIrs.getConsumptionLimitLocked();
-                        mRemainingConsumableNarcs = Math.min(consumptionLimit,
-                                parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
+                        mRemainingConsumableCakes = Math.min(consumptionLimit,
+                                parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
                                         consumptionLimit));
                         break;
                     case XML_TAG_USER:
@@ -285,11 +285,11 @@
 
     @GuardedBy("mIrs.getLock()")
     void setConsumptionLimitLocked(long limit) {
-        if (mRemainingConsumableNarcs > limit) {
-            mRemainingConsumableNarcs = limit;
+        if (mRemainingConsumableCakes > limit) {
+            mRemainingConsumableCakes = limit;
         } else if (limit > mSatiatedConsumptionLimit) {
-            final long diff = mSatiatedConsumptionLimit - mRemainingConsumableNarcs;
-            mRemainingConsumableNarcs = (limit - diff);
+            final long diff = mSatiatedConsumptionLimit - mRemainingConsumableCakes;
+            mRemainingConsumableCakes = (limit - diff);
         }
         mSatiatedConsumptionLimit = limit;
         postWrite();
@@ -306,7 +306,7 @@
         TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
         TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable);
         mLedgers.clear();
-        mRemainingConsumableNarcs = 0;
+        mRemainingConsumableCakes = 0;
         mSatiatedConsumptionLimit = 0;
         mLastReclamationTime = 0;
     }
@@ -491,8 +491,8 @@
                 out.startTag(null, XML_TAG_HIGH_LEVEL_STATE);
                 out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime);
                 out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit);
-                out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_NARCS,
-                        mRemainingConsumableNarcs);
+                out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES,
+                        mRemainingConsumableCakes);
                 out.endTag(null, XML_TAG_HIGH_LEVEL_STATE);
 
                 for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
index 78508d4..87db863 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -26,7 +26,7 @@
 import java.time.Clock;
 
 class TareUtils {
-    private static final long NARC_IN_ARC = 1_000_000_000L;
+    private static final long CAKE_IN_ARC = 1_000_000_000L;
 
     @SuppressLint("SimpleDateFormat")
     private static final SimpleDateFormat sDumpDateFormat =
@@ -35,8 +35,8 @@
     @VisibleForTesting
     static Clock sSystemClock = Clock.systemUTC();
 
-    static long arcToNarc(int arcs) {
-        return arcs * NARC_IN_ARC;
+    static long arcToCake(int arcs) {
+        return arcs * CAKE_IN_ARC;
     }
 
     static void dumpTime(IndentingPrintWriter pw, long time) {
@@ -47,26 +47,26 @@
         return sSystemClock.millis();
     }
 
-    static int narcToArc(long narcs) {
-        return (int) (narcs / NARC_IN_ARC);
+    static int cakeToArc(long cakes) {
+        return (int) (cakes / CAKE_IN_ARC);
     }
 
     @NonNull
-    static String narcToString(long narcs) {
-        if (narcs == 0) {
+    static String cakeToString(long cakes) {
+        if (cakes == 0) {
             return "0 ARCs";
         }
-        final long sub = Math.abs(narcs) % NARC_IN_ARC;
-        final long arcs = narcToArc(narcs);
+        final long sub = Math.abs(cakes) % CAKE_IN_ARC;
+        final long arcs = cakeToArc(cakes);
         if (arcs == 0) {
             return sub == 1
-                    ? sub + " narc"
-                    : sub + " narcs";
+                    ? sub + " cake"
+                    : sub + " cakes";
         }
         StringBuilder sb = new StringBuilder();
         sb.append(arcs);
         if (sub > 0) {
-            sb.append(".").append(sub / (NARC_IN_ARC / 1000));
+            sb.append(".").append(sub / (CAKE_IN_ARC / 1000));
         }
         sb.append(" ARC");
         if (arcs != 1 || sub > 0) {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index dd3ddaf..29f21f1 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -763,6 +763,14 @@
      * (for example, when apps are displayed side by side in split-screen mode
      * in landscape orientation).
      *
+     * <p>In multiple-screen scenarios, the width measurement can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device
+     * (with the screens side by side), {@code screenWidthDp} represents the
+     * width of both screens, excluding the area occupied by screen decorations.
+     * When the app is restricted to a single screen in a multiple-screen
+     * environment, {@code screenWidthDp} is the width of the screen on which
+     * the app is running.
+     *
      * <p>Differs from {@link android.view.WindowMetrics} by not including
      * screen decorations in the width measurement and by expressing the
      * measurement in dp rather than px. Use {@code screenWidthDp} to obtain the
@@ -792,6 +800,14 @@
      * (for example, when apps are displayed one above another in split-screen
      * mode in portrait orientation).
      *
+     * <p>In multiple-screen scenarios, the height measurement can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device
+     * rotated 90 degrees (one screen above the other), {@code screenHeightDp}
+     * represents the height of both screens, excluding the area occupied by
+     * screen decorations. When the app is restricted to a single screen in a
+     * multiple-screen environment, {@code screenHeightDp} is the height of the
+     * screen on which the app is running.
+     *
      * <p>Differs from {@link android.view.WindowMetrics} by not including
      * screen decorations in the height measurement and by expressing the
      * measurement in dp rather than px. Use {@code screenHeightDp} to obtain
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index aa98f1f..cf611fb 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -824,24 +824,34 @@
             if (!isExtensionSupported(mCameraId, extension, mChars)) {
                 throw new IllegalArgumentException("Unsupported extension");
             }
-            Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
-                    initializeExtension(extension);
-            extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
-            extenders.second.init(mCameraId, mChars.getNativeMetadata());
-            CameraMetadataNative captureRequestMeta =
-                    extenders.second.getAvailableCaptureRequestKeys();
+
+            CameraMetadataNative captureRequestMeta = null;
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                captureRequestMeta = extender.getAvailableCaptureRequestKeys(mCameraId);
+            } else {
+                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                        initializeExtension(extension);
+                extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+                extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
+                extenders.second.onDeInit();
+            }
 
             if (captureRequestMeta != null) {
                 int[] requestKeys = captureRequestMeta.get(
                         CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
                 if (requestKeys == null) {
-                    throw new AssertionError("android.request.availableRequestKeys must be non-null"
-                            + " in the characteristics");
+                    throw new AssertionError(
+                            "android.request.availableRequestKeys must be non-null"
+                                    + " in the characteristics");
                 }
-                CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta);
+                CameraCharacteristics requestChars = new CameraCharacteristics(
+                        captureRequestMeta);
 
                 Object crKey = CaptureRequest.Key.class;
-                Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+                Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>) crKey;
 
                 ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
                         requestKeys, /*includeSynthetic*/ false));
@@ -854,7 +864,6 @@
             if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
                 ret.add(CaptureRequest.JPEG_ORIENTATION);
             }
-            extenders.second.onDeInit();
         } catch (RemoteException e) {
             throw new IllegalStateException("Failed to query the available capture request keys!");
         } finally {
@@ -894,12 +903,19 @@
                 throw new IllegalArgumentException("Unsupported extension");
             }
 
-            Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
-                    initializeExtension(extension);
-            extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
-            extenders.second.init(mCameraId, mChars.getNativeMetadata());
-            CameraMetadataNative captureResultMeta =
-                    extenders.second.getAvailableCaptureResultKeys();
+            CameraMetadataNative captureResultMeta = null;
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                captureResultMeta = extender.getAvailableCaptureResultKeys(mCameraId);
+            } else {
+                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                        initializeExtension(extension);
+                extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+                extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
+                extenders.second.onDeInit();
+            }
 
             if (captureResultMeta != null) {
                 int[] resultKeys = captureResultMeta.get(
@@ -926,7 +942,6 @@
                     ret.add(CaptureResult.SENSOR_TIMESTAMP);
                 }
             }
-            extenders.second.onDeInit();
         } catch (RemoteException e) {
             throw new IllegalStateException("Failed to query the available capture result keys!");
         } finally {
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index ee3441f..6ddaddf 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -265,8 +265,8 @@
      * from the camera device, to produce a single high-quality output result.
      *
      * <p>Note that single capture requests currently do not support
-     * client parameters except for {@link CaptureRequest#JPEG_ORIENTATION orientation} and
-     * {@link CaptureRequest#JPEG_QUALITY quality} in case of ImageFormat.JPEG output target.
+     * client parameters except for controls advertised in
+     * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
      * The rest of the settings included in the request will be entirely overridden by
      * the device-specific extension. </p>
      *
@@ -275,6 +275,11 @@
      * arguments that include further targets will cause
      * IllegalArgumentException to be thrown. </p>
      *
+     * <p>Starting with Android {@link android.os.Build.VERSION_CODES#TIRAMISU} single capture
+     * requests will also support the preview {@link android.graphics.ImageFormat#PRIVATE} target
+     * surface. These can typically be used for enabling AF/AE triggers. Do note, that single
+     * capture requests referencing both output surfaces remain unsupported.</p>
+     *
      * <p>Each request will produce one new frame for one target Surface, set
      * with the CaptureRequest builder's
      * {@link CaptureRequest.Builder#addTarget} method.</p>
@@ -319,8 +324,10 @@
      * rate possible.</p>
      *
      * <p>Note that repeating capture requests currently do not support
-     * client parameters. Settings included in the request will
-     * be completely overridden by the device-specific extension.</p>
+     * client parameters except for controls advertised in
+     * {@link CameraExtensionCharacteristics#getAvailableCaptureRequestKeys}.
+     * The rest of the settings included in the request will be entirely overridden by
+     * the device-specific extension. </p>
      *
      * <p>The {@link CaptureRequest.Builder#addTarget} supports only one
      * target surface. {@link CaptureRequest} arguments that include further
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
index f279c59..935a542 100644
--- a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -19,6 +19,7 @@
 import android.hardware.camera2.extension.LatencyRange;
 import android.hardware.camera2.extension.Size;
 import android.hardware.camera2.extension.SizeList;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
 /** @hide */
 interface IAdvancedExtenderImpl
@@ -30,4 +31,6 @@
     @nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
     @nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
     ISessionProcessorImpl getSessionProcessor();
+    CameraMetadataNative getAvailableCaptureRequestKeys(in String cameraId);
+    CameraMetadataNative getAvailableCaptureResultKeys(in String cameraId);
 }
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
index 6ab0ad2..f3062ad 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -16,6 +16,7 @@
 package android.hardware.camera2.extension;
 
 import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.impl.CameraMetadataNative;
 
 /** @hide */
 interface ICaptureCallback
@@ -25,4 +26,5 @@
     void onCaptureFailed(int captureSequenceId);
     void onCaptureSequenceCompleted(int captureSequenceId);
     void onCaptureSequenceAborted(int captureSequenceId);
+    void onCaptureCompleted(long shutterTimestamp, int requestId, in CameraMetadataNative results);
 }
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
index 6fdf4df..0eca5a7 100644
--- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.camera2.extension;
 
+import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.extension.CameraSessionConfig;
 import android.hardware.camera2.extension.ICaptureCallback;
 import android.hardware.camera2.extension.IRequestProcessorImpl;
@@ -30,5 +31,7 @@
     void onCaptureSessionEnd();
     int startRepeating(in ICaptureCallback callback);
     void stopRepeating();
-    int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
+    int startCapture(in ICaptureCallback callback);
+    void setParameters(in CaptureRequest captureRequest);
+    int startTrigger(in CaptureRequest captureRequest, in ICaptureCallback callback);
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 3c52d65..5503e28 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -86,6 +86,7 @@
     // maps camera extension output ids to camera registered image readers
     private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
     private final RequestProcessor mRequestProcessor = new RequestProcessor();
+    private final int mSessionId;
 
     private Surface mClientRepeatingRequestSurface;
     private Surface mClientCaptureSurface;
@@ -175,7 +176,7 @@
 
         CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
                 extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
-                config.getStateCallback(), config.getExecutor());
+                config.getStateCallback(), config.getExecutor(), sessionId);
         ret.initialize();
 
         return ret;
@@ -184,7 +185,8 @@
     private CameraAdvancedExtensionSessionImpl(long extensionClientId,
             @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
             @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
-            @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
+            @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor,
+            int sessionId) {
         mExtensionClientId = extensionClientId;
         mAdvancedExtender = extender;
         mCameraDevice = cameraDevice;
@@ -197,6 +199,7 @@
         mHandler = new Handler(mHandlerThread.getLooper());
         mInitialized = false;
         mInitializeHandler = new InitializeSessionHandler();
+        mSessionId = sessionId;
     }
 
     /**
@@ -367,6 +370,8 @@
             }
 
             try {
+                mSessionProcessor.setParameters(request);
+
                 seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
                         executor, listener));
             } catch (RemoteException e) {
@@ -388,35 +393,33 @@
                 throw new IllegalStateException("Uninitialized component");
             }
 
-            if (mClientCaptureSurface == null) {
-                throw new IllegalArgumentException("No output surface registered for single"
-                        + " requests!");
+            if (request.getTargets().size() != 1) {
+                throw new IllegalArgumentException("Single capture to both preview & still"  +
+                        " capture outputs target is not supported!");
             }
 
-            if (!request.containsTarget(mClientCaptureSurface) ||
-                    (request.getTargets().size() != 1)) {
+            if ((mClientCaptureSurface != null)  && request.containsTarget(mClientCaptureSurface)) {
+                try {
+                    mSessionProcessor.setParameters(request);
+
+                    seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
+                            executor, listener));
+                } catch (RemoteException e) {
+                    throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
+                            " to submit capture request, extension service failed to respond!");
+                }
+            } else if ((mClientRepeatingRequestSurface != null) &&
+                    request.containsTarget(mClientRepeatingRequestSurface)) {
+                try {
+                    seqId = mSessionProcessor.startTrigger(request,
+                            new RequestCallbackHandler(request, executor, listener));
+                } catch (RemoteException e) {
+                    throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
+                            " to submit trigger request, extension service failed to respond!");
+                }
+            } else {
                 throw new IllegalArgumentException("Invalid single capture output target!");
             }
-
-            try {
-                // This will override the extension capture stage jpeg parameters with the user set
-                // jpeg quality and rotation. This will guarantee that client configured jpeg
-                // parameters always have highest priority.
-                Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
-                if (jpegRotation == null) {
-                    jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
-                }
-                Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
-                if (jpegQuality == null) {
-                    jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
-                }
-
-                seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
-                        executor, listener), jpegRotation, jpegQuality);
-            } catch (RemoteException e) {
-                throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
-                        "Failed to submit capture request, extension service failed to respond!");
-            }
         }
 
         return seqId;
@@ -661,6 +664,28 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
+
+        @Override
+        public void onCaptureCompleted(long timestamp, int requestId, CameraMetadataNative result) {
+            if (result == null) {
+                Log.e(TAG,"Invalid capture result!");
+                return;
+            }
+
+            result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+            TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
+                    mClientRequest, requestId, timestamp, new ArrayList<>(), mSessionId,
+                    new PhysicalCaptureResultInfo[0]);
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(
+                        () -> mClientCallbacks.onCaptureResultAvailable(
+                                CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+                                totalResult));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 1514a2b..aee20db 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -303,6 +303,7 @@
                     jpegBuffer, jpegCapacity, jpegParams.mQuality,
                     0, 0, yuvImage.getWidth(), yuvImage.getHeight(),
                     jpegParams.mRotation);
+            jpegImage.setTimestamp(yuvImage.getTimestamp());
             yuvImage.close();
 
             try {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 916d16d..1263da6 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -475,8 +475,8 @@
             mInternalRepeatingRequestEnabled = false;
             try {
                 return setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                        new RepeatingRequestHandler(request, executor, listener,
-                                mRepeatingRequestImageCallback));
+                        new PreviewRequestHandler(request, executor, listener,
+                                mRepeatingRequestImageCallback), request);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to set repeating request! Extension service does not "
                         + "respond");
@@ -530,7 +530,9 @@
             CaptureRequest request = requestBuilder.build();
             CameraMetadataNative.update(request.getNativeMetadata(), captureStage.parameters);
             ret.add(request);
-            captureMap.put(request, captureStage.id);
+            if (captureMap != null) {
+                captureMap.put(request, captureStage.id);
+            }
         }
 
         return ret;
@@ -583,33 +585,57 @@
             throw new IllegalStateException("Uninitialized component");
         }
 
-        if (mClientCaptureSurface == null) {
-            throw new IllegalArgumentException("No output surface registered for single requests!");
+        if (request.getTargets().size() != 1) {
+            throw new IllegalArgumentException("Single capture to both preview & still capture " +
+                    "outputs target is not supported!");
         }
 
-        if (!request.containsTarget(mClientCaptureSurface) || (request.getTargets().size() != 1)) {
-            throw new IllegalArgumentException("Invalid single capture output target!");
+        int seqId = -1;
+        if ((mClientCaptureSurface != null) && request.containsTarget(mClientCaptureSurface)) {
+            HashMap<CaptureRequest, Integer> requestMap = new HashMap<>();
+            List<CaptureRequest> burstRequest;
+            try {
+                burstRequest = createBurstRequest(mCameraDevice,
+                        mImageExtender.getCaptureStages(), request, mCameraBurstSurface,
+                        CameraDevice.TEMPLATE_STILL_CAPTURE, requestMap);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to initialize internal burst request! Extension service does"
+                        + " not respond!");
+                throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
+            }
+            if (burstRequest == null) {
+                throw new UnsupportedOperationException(
+                        "Failed to create still capture burst request");
+            }
+
+            seqId =  mCaptureSession.captureBurstRequests(burstRequest,
+                    new CameraExtensionUtils.HandlerExecutor(mHandler),
+                    new BurstRequestHandler(request, executor, listener, requestMap,
+                            mBurstCaptureImageCallback));
+        } else if ((mClientRepeatingRequestSurface != null) &&
+                request.containsTarget(mClientRepeatingRequestSurface)) {
+
+            CaptureRequest captureRequest = null;
+            try {
+                ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
+                captureStageList.add(mPreviewExtender.getCaptureStage());
+
+                captureRequest = createRequest(mCameraDevice, captureStageList,
+                        mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, request);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to initialize capture request! Extension service does"
+                        + " not respond!");
+                throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
+            }
+
+            seqId = mCaptureSession.capture(captureRequest, new PreviewRequestHandler(request,
+                    executor, listener, mRepeatingRequestImageCallback, true /*singleCapture*/),
+                    mHandler);
+        } else {
+            throw new IllegalArgumentException("Capture request to unknown output surface!");
         }
 
-        HashMap<CaptureRequest, Integer> requestMap = new HashMap<>();
-        List<CaptureRequest> burstRequest;
-        try {
-            burstRequest = createBurstRequest(mCameraDevice,
-                    mImageExtender.getCaptureStages(), request, mCameraBurstSurface,
-                    CameraDevice.TEMPLATE_STILL_CAPTURE, requestMap);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to initialize internal burst request! Extension service does"
-                    + " not respond!");
-            throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
-        }
-        if (burstRequest == null) {
-            throw new UnsupportedOperationException("Failed to create still capture burst request");
-        }
-
-        return mCaptureSession.captureBurstRequests(burstRequest,
-                new CameraExtensionUtils.HandlerExecutor(mHandler),
-                new BurstRequestHandler(request, executor, listener, requestMap,
-                        mBurstCaptureImageCallback));
+        return seqId;
     }
 
     @Override
@@ -847,7 +873,7 @@
             } else {
                 try {
                     setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                            new RepeatingRequestHandler(null, null, null,
+                            new PreviewRequestHandler(null, null, null,
                                     mRepeatingRequestImageCallback));
                 } catch (CameraAccessException | RemoteException e) {
                     Log.e(TAG,
@@ -1010,7 +1036,7 @@
             if (timestamp != null) {
                 if (mCaptureResultsSupported && (mCaptureResultHandler == null)) {
                     mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
-                            mCallbacks, result.getSessionId());
+                            mCallbacks, result.getSequenceId());
                 }
                 if (mImageProcessor != null) {
                     if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
@@ -1179,7 +1205,7 @@
                  */
                 try {
                     setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                            new RepeatingRequestHandler(null, null, null,
+                            new PreviewRequestHandler(null, null, null,
                                     mImageCallback));
                 } catch (CameraAccessException | RemoteException e) {
                     Log.e(TAG, "Failed to start the internal repeating request!");
@@ -1320,17 +1346,20 @@
         }
     }
 
-    // This handler can operate in two modes:
+    // This handler can operate in three modes:
     // 1) Using valid client callbacks, which means camera buffers will be propagated the
     //    registered output surfaces and clients will be notified accordingly.
     // 2) Without any client callbacks where an internal repeating request is kept active
     //    to satisfy the extensions continuous preview/(repeating request) requirement.
-    private class RepeatingRequestHandler extends CameraCaptureSession.CaptureCallback {
+    // 3) Single capture mode, where internal repeating requests are ignored and the preview
+    //    logic is only triggered for the image processor case.
+    private class PreviewRequestHandler extends CameraCaptureSession.CaptureCallback {
         private final Executor mExecutor;
         private final ExtensionCaptureCallback mCallbacks;
         private final CaptureRequest mClientRequest;
         private final boolean mClientNotificationsEnabled;
         private final CameraOutputImageCallback mRepeatingImageCallback;
+        private final boolean mSingleCapture;
         private OnImageAvailableListener mImageCallback = null;
         private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap =
                 new LongSparseArray<>();
@@ -1338,15 +1367,22 @@
 
         private boolean mRequestUpdatedNeeded = false;
 
-        public RepeatingRequestHandler(@Nullable CaptureRequest clientRequest,
+        public PreviewRequestHandler(@Nullable CaptureRequest clientRequest,
                 @Nullable Executor executor, @Nullable ExtensionCaptureCallback listener,
                 @NonNull CameraOutputImageCallback imageCallback) {
+            this(clientRequest, executor, listener, imageCallback, false /*singleCapture*/);
+        }
+
+        public PreviewRequestHandler(@Nullable CaptureRequest clientRequest,
+                @Nullable Executor executor, @Nullable ExtensionCaptureCallback listener,
+                @NonNull CameraOutputImageCallback imageCallback, boolean singleCapture) {
             mClientRequest = clientRequest;
             mExecutor = executor;
             mCallbacks = listener;
             mClientNotificationsEnabled =
                     (mClientRequest != null) && (mExecutor != null) && (mCallbacks != null);
             mRepeatingImageCallback = imageCallback;
+            mSingleCapture = singleCapture;
         }
 
         @Override
@@ -1393,7 +1429,7 @@
         public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
                                              int sequenceId) {
             synchronized (mInterfaceLock) {
-                if (mInternalRepeatingRequestEnabled) {
+                if (mInternalRepeatingRequestEnabled && !mSingleCapture) {
                     resumeInternalRepeatingRequest(true);
                 }
             }
@@ -1420,10 +1456,10 @@
                                                long frameNumber) {
 
             synchronized (mInterfaceLock) {
-                if (mRequestUpdatedNeeded) {
+                if (mRequestUpdatedNeeded && !mSingleCapture) {
                     mRequestUpdatedNeeded = false;
                     resumeInternalRepeatingRequest(false);
-                } else if (mInternalRepeatingRequestEnabled) {
+                } else if (mInternalRepeatingRequestEnabled && !mSingleCapture) {
                     resumeInternalRepeatingRequest(true);
                 }
             }
@@ -1471,10 +1507,10 @@
                     if (mCaptureResultsSupported && mClientNotificationsEnabled &&
                             (mCaptureResultHandler == null)) {
                         mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
-                                mCallbacks, result.getSessionId());
+                                mCallbacks, result.getSequenceId());
                     }
-                    if (mPreviewProcessorType ==
-                            IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
+                    if ((!mSingleCapture) && (mPreviewProcessorType ==
+                            IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY)) {
                         CaptureStageImpl captureStage = null;
                         try {
                             captureStage = mPreviewRequestUpdateProcessor.process(
@@ -1582,10 +1618,10 @@
             try {
                 if (internal) {
                     setRepeatingRequest(mPreviewExtender.getCaptureStage(),
-                            new RepeatingRequestHandler(null, null, null,
+                            new PreviewRequestHandler(null, null, null,
                                     mRepeatingImageCallback));
                 } else {
-                    setRepeatingRequest(mPreviewExtender.getCaptureStage(), this);
+                    setRepeatingRequest(mPreviewExtender.getCaptureStage(), this, mClientRequest);
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to resume internal repeating request, extension service"
@@ -1733,7 +1769,7 @@
     }
 
     private static Size findSmallestAspectMatchedSize(@NonNull List<Size> sizes,
-                                                      @NonNull Size arSize) {
+            @NonNull Size arSize) {
         final float TOLL = .01f;
 
         if (arSize.getHeight() == 0) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index cc93adc..0bed342 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2280,8 +2280,11 @@
     }
 
     /**
-     * {@link #getX(int)} for the first pointer index (may be an
-     * arbitrary pointer identifier).
+     * Equivalent to {@link #getX(int)} for pointer index 0 (regardless of the
+     * pointer identifier).
+     *
+     * @return The X coordinate of the first pointer index in the coordinate
+     *      space of the view that received this motion event.
      *
      * @see #AXIS_X
      */
@@ -2290,8 +2293,11 @@
     }
 
     /**
-     * {@link #getY(int)} for the first pointer index (may be an
-     * arbitrary pointer identifier).
+     * Equivalent to {@link #getY(int)} for pointer index 0 (regardless of the
+     * pointer identifier).
+     *
+     * @return The Y coordinate of the first pointer index in the coordinate
+     *      space of the view that received this motion event.
      *
      * @see #AXIS_Y
      */
@@ -2433,13 +2439,20 @@
     }
 
     /**
-     * Returns the X coordinate of this event for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * Whole numbers are pixels; the
-     * value may have a fraction for input devices that are sub-pixel precise.
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * Returns the X coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the view that received this motion event.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the X coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The X coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #AXIS_X
      */
@@ -2448,13 +2461,20 @@
     }
 
     /**
-     * Returns the Y coordinate of this event for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * Whole numbers are pixels; the
-     * value may have a fraction for input devices that are sub-pixel precise.
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * Returns the Y coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the view that received this motion event.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the Y coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The Y coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #AXIS_Y
      */
@@ -2700,12 +2720,13 @@
     }
 
     /**
-     * Returns the original raw X coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Equivalent to {@link #getRawX(int)} for pointer index 0 (regardless of
+     * the pointer identifier).
      *
-     * @see #getX(int)
+     * @return The X coordinate of the first pointer index in the coordinate
+     *      space of the device display.
+     *
+     * @see #getX()
      * @see #AXIS_X
      */
     public final float getRawX() {
@@ -2713,12 +2734,13 @@
     }
 
     /**
-     * Returns the original raw Y coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Equivalent to {@link #getRawY(int)} for pointer index 0 (regardless of
+     * the pointer identifier).
      *
-     * @see #getY(int)
+     * @return The Y coordinate of the first pointer index in the coordinate
+     *      space of the device display.
+     *
+     * @see #getY()
      * @see #AXIS_Y
      */
     public final float getRawY() {
@@ -2726,13 +2748,38 @@
     }
 
     /**
-     * Returns the original raw X coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Returns the X coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the device display, irrespective of system
+     * decorations and whether or not the system is in multi-window mode. If the
+     * app spans multiple screens in a multiple-screen environment, the
+     * coordinate space includes all of the spanned screens.
      *
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * <p>In multi-window mode, the coordinate space extends beyond the bounds
+     * of the app window to encompass the entire display area. For example, if
+     * the motion event occurs in the right-hand window of split-screen mode in
+     * landscape orientation, the left edge of the screen&mdash;not the left
+     * edge of the window&mdash;is the origin from which the X coordinate is
+     * calculated.
+     *
+     * <p>In multiple-screen scenarios, the coordinate space can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device,
+     * and the motion event occurs on the right-hand screen, the X coordinate is
+     * calculated from the left edge of the left-hand screen to the point of the
+     * motion event on the right-hand screen. When the app is restricted to a
+     * single screen in a multiple-screen environment, the coordinate space
+     * includes only the screen on which the app is running.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the X coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The X coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #getX(int)
      * @see #AXIS_X
@@ -2742,13 +2789,38 @@
     }
 
     /**
-     * Returns the original raw Y coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Returns the Y coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the device display, irrespective of system
+     * decorations and whether or not the system is in multi-window mode. If the
+     * app spans multiple screens in a multiple-screen environment, the
+     * coordinate space includes all of the spanned screens.
      *
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * <p>In multi-window mode, the coordinate space extends beyond the bounds
+     * of the app window to encompass the entire device screen. For example, if
+     * the motion event occurs in the lower window of split-screen mode in
+     * portrait orientation, the top edge of the screen&mdash;not the top edge
+     * of the window&mdash;is the origin from which the Y coordinate is
+     * determined.
+     *
+     * <p>In multiple-screen scenarios, the coordinate space can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device
+     * that's rotated 90 degrees, and the motion event occurs on the lower
+     * screen, the Y coordinate is calculated from the top edge of the upper
+     * screen to the point of the motion event on the lower screen. When the app
+     * is restricted to a single screen in a multiple-screen environment, the
+     * coordinate space includes only the screen on which the app is running.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the Y coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The Y coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #getY(int)
      * @see #AXIS_Y
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 757f409..b23d8ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1864,6 +1864,11 @@
      -->
     <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
 
+    <!-- The package name of the default search selector app. Must be granted the POST_NOTIFICATIONS
+         permission.
+    -->
+    <string name="config_defaultSearchSelectorPackageName" translatable="false"></string>
+
     <!-- Whether to enable geocoder overlay which allows geocoder to be replaced
          by an app at run-time. When disabled, only the
          config_geocoderProviderPackageName package will be searched for
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3334822..21b2351 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3413,6 +3413,9 @@
   <!-- Network Recommendation -->
   <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
 
+  <!-- Search Selector -->
+  <java-symbol type="string" name="config_defaultSearchSelectorPackageName" />
+
   <!-- Optional IPsec algorithms -->
   <java-symbol type="array" name="config_optionalIpSecAlgorithms" />
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index e7a23f2..43fba52 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -516,8 +516,6 @@
         onView(withText(R.string.resolver_work_tab))
                 .perform(click());
         waitForIdle();
-        // wait for the share sheet to expand
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
         onView(first(allOf(withText(workResolvedComponentInfos.get(0)
                 .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
                 .perform(click());
@@ -616,8 +614,6 @@
         onView(withText(R.string.resolver_work_tab))
                 .perform(click());
         waitForIdle();
-        // wait for the share sheet to expand
-        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
         onView(first(allOf(
                 withText(workResolvedComponentInfos.get(0)
                         .getResolveInfoAt(0).activityInfo.applicationInfo.name),
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f7acda7e..6a5416d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2283,6 +2283,10 @@
     <string name="media_output_broadcast_code">Password</string>
     <!-- Button for change broadcast name and broadcast code [CHAR LIMIT=60] -->
     <string name="media_output_broadcast_dialog_save">Save</string>
+    <!-- The "starting" text when Broadcast is starting [CHAR LIMIT=60] -->
+    <string name="media_output_broadcast_starting">Starting&#8230;</string>
+    <!-- The button text when Broadcast start failed [CHAR LIMIT=60] -->
+    <string name="media_output_broadcast_start_failed">Can\u2019t broadcast</string>
 
     <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
     <string name="build_number_clip_data_label">Build number</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c0ba51f..f435579 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -860,7 +860,7 @@
         private void setupUserSwitcher() {
             final UserRecord currentUser = mUserSwitcherController.getCurrentUserRecord();
             if (currentUser == null) {
-                Log.wtf(TAG, "Current user in user switcher is null.");
+                Log.e(TAG, "Current user in user switcher is null.");
                 return;
             }
             Drawable userIcon = findUserIcon(currentUser.info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index bde772d..e5e7eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -19,7 +19,10 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.statusBars;
 
+import android.annotation.NonNull;
 import android.app.WallpaperColors;
+import android.bluetooth.BluetoothLeBroadcast;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
@@ -59,6 +62,9 @@
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 /**
  * Base dialog for media output UI
  */
@@ -69,6 +75,7 @@
     private static final String EMPTY_TITLE = " ";
     private static final String PREF_NAME = "MediaOutputDialog";
     private static final String PREF_IS_LE_BROADCAST_FIRST_LAUNCH = "PrefIsLeBroadcastFirstLaunch";
+    private static final boolean DEBUG = true;
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
     private final RecyclerView.LayoutManager mLayoutManager;
@@ -91,6 +98,7 @@
     private Button mAppButton;
     private int mListMaxHeight;
     private WallpaperColors mWallpaperColors;
+    private Executor mExecutor;
 
     MediaOutputBaseAdapter mAdapter;
 
@@ -103,6 +111,79 @@
         }
     };
 
+    private final BluetoothLeBroadcast.Callback mBroadcastCallback =
+            new BluetoothLeBroadcast.Callback() {
+                @Override
+                public void onBroadcastStarted(int reason, int broadcastId) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastStarted(), reason = " + reason
+                                + ", broadcastId = " + broadcastId);
+                    }
+                    mMainThreadHandler.post(() -> startLeBroadcastDialog());
+                }
+
+                @Override
+                public void onBroadcastStartFailed(int reason) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastStartFailed(), reason = " + reason);
+                    }
+                    handleLeBroadcastStartFailed();
+                }
+
+                @Override
+                public void onBroadcastMetadataChanged(int broadcastId,
+                        @NonNull BluetoothLeBroadcastMetadata metadata) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId
+                                + ", metadata = " + metadata);
+                    }
+                    mMainThreadHandler.post(() -> refresh());
+                }
+
+                @Override
+                public void onBroadcastStopped(int reason, int broadcastId) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastStopped(), reason = " + reason
+                                + ", broadcastId = " + broadcastId);
+                    }
+                    mMainThreadHandler.post(() -> refresh());
+                }
+
+                @Override
+                public void onBroadcastStopFailed(int reason) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastStopFailed(), reason = " + reason);
+                    }
+                    mMainThreadHandler.post(() -> refresh());
+                }
+
+                @Override
+                public void onBroadcastUpdated(int reason, int broadcastId) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastUpdated(), reason = " + reason
+                                + ", broadcastId = " + broadcastId);
+                    }
+                    mMainThreadHandler.post(() -> refresh());
+                }
+
+                @Override
+                public void onBroadcastUpdateFailed(int reason, int broadcastId) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onBroadcastUpdateFailed(), reason = " + reason
+                                + ", broadcastId = " + broadcastId);
+                    }
+                    mMainThreadHandler.post(() -> refresh());
+                }
+
+                @Override
+                public void onPlaybackStarted(int reason, int broadcastId) {
+                }
+
+                @Override
+                public void onPlaybackStopped(int reason, int broadcastId) {
+                }
+            };
+
     public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
             MediaOutputController mediaOutputController) {
         super(context, R.style.Theme_SystemUI_Dialog_Media);
@@ -114,6 +195,7 @@
         mLayoutManager = new LinearLayoutManager(mContext);
         mListMaxHeight = context.getResources().getDimensionPixelSize(
                 R.dimen.media_output_dialog_list_max_height);
+        mExecutor = Executors.newSingleThreadExecutor();
     }
 
     @Override
@@ -171,11 +253,18 @@
     public void onStart() {
         super.onStart();
         mMediaOutputController.start(this);
+        if(isBroadcastSupported()) {
+            mMediaOutputController.registerLeBroadcastServiceCallBack(mExecutor,
+                    mBroadcastCallback);
+        }
     }
 
     @Override
     public void onStop() {
         super.onStop();
+        if(isBroadcastSupported()) {
+            mMediaOutputController.unregisterLeBroadcastServiceCallBack(mBroadcastCallback);
+        }
         mMediaOutputController.stop();
     }
 
@@ -254,35 +343,12 @@
                 mAdapter.notifyDataSetChanged();
             }
         }
-        // Show when remote media session is available
+        // Show when remote media session is available or
+        //      when the device supports BT LE audio + media is playing
         mStopButton.setVisibility(getStopButtonVisibility());
-        if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
-            mStopButton.setText(R.string.media_output_broadcast);
-            mStopButton.setOnClickListener(v -> {
-                SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
-                        Context.MODE_PRIVATE);
-
-                if (sharedPref != null
-                        && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
-                    Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");
-
-                    mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
-                            mBroadcastSender,
-                            MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH);
-                    SharedPreferences.Editor editor = sharedPref.edit();
-                    editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
-                    editor.apply();
-                } else {
-                    mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
-                            mBroadcastSender);
-                }
-            });
-        } else {
-            mStopButton.setOnClickListener(v -> {
-                mMediaOutputController.releaseSession();
-                dismiss();
-            });
-        }
+        mStopButton.setEnabled(true);
+        mStopButton.setText(getStopButtonText());
+        mStopButton.setOnClickListener(v -> onStopButtonClick());
     }
 
     private Drawable resizeDrawable(Drawable drawable, int size) {
@@ -301,6 +367,56 @@
                 Bitmap.createScaledBitmap(bitmap, size, size, false));
     }
 
+    protected void handleLeBroadcastStartFailed() {
+        mStopButton.setText(R.string.media_output_broadcast_start_failed);
+        mStopButton.setEnabled(false);
+        mMainThreadHandler.postDelayed(() -> refresh(), 3000);
+    }
+
+    protected void startLeBroadcast() {
+        mStopButton.setText(R.string.media_output_broadcast_starting);
+        mStopButton.setEnabled(false);
+        if (!mMediaOutputController.startBluetoothLeBroadcast()) {
+            // If the system can't execute "broadcast start", then UI shows the error.
+            handleLeBroadcastStartFailed();
+        }
+    }
+
+    protected boolean startLeBroadcastDialogForFirstTime(){
+        SharedPreferences sharedPref = mContext.getSharedPreferences(PREF_NAME,
+                Context.MODE_PRIVATE);
+        if (sharedPref != null
+                && sharedPref.getBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, true)) {
+            Log.d(TAG, "PREF_IS_LE_BROADCAST_FIRST_LAUNCH: true");
+
+            mMediaOutputController.launchLeBroadcastNotifyDialog(mDialogView,
+                    mBroadcastSender,
+                    MediaOutputController.BroadcastNotifyDialog.ACTION_FIRST_LAUNCH,
+                    (d, w) -> {
+                        startLeBroadcast();
+                    });
+            SharedPreferences.Editor editor = sharedPref.edit();
+            editor.putBoolean(PREF_IS_LE_BROADCAST_FIRST_LAUNCH, false);
+            editor.apply();
+            return true;
+        }
+        return false;
+    }
+
+    protected void startLeBroadcastDialog() {
+        mMediaOutputController.launchMediaOutputBroadcastDialog(mDialogView,
+                mBroadcastSender);
+        refresh();
+    }
+
+    protected void stopLeBroadcast() {
+        mStopButton.setEnabled(false);
+        if (!mMediaOutputController.stopBluetoothLeBroadcast()) {
+            // If the system can't execute "broadcast stop", then UI does refresh.
+            mMainThreadHandler.post(() -> refresh());
+        }
+    }
+
     abstract Drawable getAppSourceIcon();
 
     abstract int getHeaderIconRes();
@@ -315,6 +431,15 @@
 
     abstract int getStopButtonVisibility();
 
+    public CharSequence getStopButtonText() {
+        return mContext.getText(R.string.keyboard_key_media_stop);
+    }
+
+    public void onStopButtonClick() {
+        mMediaOutputController.releaseSession();
+        dismiss();
+    }
+
     public boolean isBroadcastSupported() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 494dae0..9b3b3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -142,8 +142,11 @@
 
         mBroadcastNotify = getDialogView().requireViewById(R.id.broadcast_info);
         mBroadcastNotify.setOnClickListener(v -> {
-            mMediaOutputController.launchLeBroadcastNotifyDialog(null, null,
-                    MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON);
+            mMediaOutputController.launchLeBroadcastNotifyDialog(
+                    /* view= */ null,
+                    /* broadcastSender= */ null,
+                    MediaOutputController.BroadcastNotifyDialog.ACTION_BROADCAST_INFO_ICON,
+                    /* onClickListener= */ null);
         });
         mBroadcastName = getDialogView().requireViewById(R.id.broadcast_name_summary);
         mBroadcastNameEdit = getDialogView().requireViewById(R.id.broadcast_name_edit);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index ec2a950..0fbec3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -18,10 +18,13 @@
 
 import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
 
+import android.annotation.CallbackExecutor;
 import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.WallpaperColors;
+import android.bluetooth.BluetoothLeBroadcast;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -58,7 +61,7 @@
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.media.BluetoothMediaDevice;
 import com.android.settingslib.media.InfoMediaManager;
@@ -83,6 +86,7 @@
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -642,17 +646,14 @@
     }
 
     void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
-            BroadcastNotifyDialog action) {
+            BroadcastNotifyDialog action, final DialogInterface.OnClickListener listener) {
         final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
         switch (action) {
             case ACTION_FIRST_LAUNCH:
                 builder.setTitle(R.string.media_output_first_broadcast_title);
                 builder.setMessage(R.string.media_output_first_notify_broadcast_message);
                 builder.setNegativeButton(android.R.string.cancel, null);
-                builder.setPositiveButton(R.string.media_output_broadcast,
-                        (d, w) -> {
-                            launchMediaOutputBroadcastDialog(mediaOutputDialog, broadcastSender);
-                        });
+                builder.setPositiveButton(R.string.media_output_broadcast, listener);
                 break;
             case ACTION_BROADCAST_INFO_ICON:
                 builder.setTitle(R.string.media_output_broadcast);
@@ -685,18 +686,64 @@
                 || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
     }
 
-    boolean isBluetoothLeDevice(@NonNull MediaDevice device) {
-        if (device instanceof BluetoothMediaDevice) {
-            final CachedBluetoothDevice cachedDevice =
-                    ((BluetoothMediaDevice) device).getCachedDevice();
-            boolean isConnectedLeAudioDevice =
-                    (cachedDevice != null) ? cachedDevice.isConnectedLeAudioDevice() : false;
-            if (DEBUG) {
-                Log.d(TAG, "isConnectedLeAudioDevice=" + isConnectedLeAudioDevice);
-            }
-            return isConnectedLeAudioDevice;
+    boolean isBroadcastSupported() {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        return broadcast != null ? true : false;
+    }
+
+    boolean isBluetoothLeBroadcastEnabled() {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            return false;
         }
-        return false;
+        return broadcast.isEnabled(null);
+    }
+
+    boolean startBluetoothLeBroadcast() {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            Log.d(TAG, "The broadcast profile is null");
+            return false;
+        }
+        broadcast.startBroadcast(getAppSourceName(), /*language*/ null);
+        return true;
+    }
+
+    boolean stopBluetoothLeBroadcast() {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            Log.d(TAG, "The broadcast profile is null");
+            return false;
+        }
+        broadcast.stopLatestBroadcast();
+        return true;
+    }
+
+    void registerLeBroadcastServiceCallBack(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull BluetoothLeBroadcast.Callback callback) {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            Log.d(TAG, "The broadcast profile is null");
+            return;
+        }
+        broadcast.registerServiceCallBack(executor, callback);
+    }
+
+    void unregisterLeBroadcastServiceCallBack(
+            @NonNull BluetoothLeBroadcast.Callback callback) {
+        LocalBluetoothLeBroadcast broadcast =
+                mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (broadcast == null) {
+            Log.d(TAG, "The broadcast profile is null");
+            return;
+        }
+        broadcast.unregisterServiceCallBack(callback);
     }
 
     private boolean isPlayBackInfoLocal() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index fc10397..9248433 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -27,7 +27,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.dagger.SysUISingleton;
@@ -93,18 +92,41 @@
             isActiveRemoteDevice = mMediaOutputController.isActiveRemoteDevice(
                     mMediaOutputController.getCurrentConnectedMediaDevice());
         }
-        boolean isBroadCastSupported = isBroadcastSupported();
+        boolean showBroadcastButton = isBroadcastSupported() && mMediaOutputController.isPlaying();
 
-        return (isActiveRemoteDevice || isBroadCastSupported) ? View.VISIBLE : View.GONE;
+        return (isActiveRemoteDevice || showBroadcastButton) ? View.VISIBLE : View.GONE;
     }
 
     @Override
     public boolean isBroadcastSupported() {
-        MediaDevice device = mMediaOutputController.getCurrentConnectedMediaDevice();
-        if (device == null) {
-            return false;
+        return mMediaOutputController.isBroadcastSupported();
+    }
+
+    @Override
+    public CharSequence getStopButtonText() {
+        int resId = R.string.keyboard_key_media_stop;
+        if (isBroadcastSupported() && mMediaOutputController.isPlaying()
+                && !mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
+            resId = R.string.media_output_broadcast;
         }
-        return mMediaOutputController.isBluetoothLeDevice(device);
+        return mContext.getText(resId);
+    }
+
+    @Override
+    public void onStopButtonClick() {
+        if (isBroadcastSupported() && mMediaOutputController.isPlaying()) {
+            if (!mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
+                if (startLeBroadcastDialogForFirstTime()) {
+                    return;
+                }
+                startLeBroadcast();
+            } else {
+                stopLeBroadcast();
+            }
+        } else {
+            mMediaOutputController.releaseSession();
+            dismiss();
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 380fa6d..7c53388 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -26,18 +27,23 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -50,6 +56,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 
 @SmallTest
@@ -61,8 +69,14 @@
 
     // Mock
     private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
+    private MediaController mMediaController = mock(MediaController.class);
+    private PlaybackState mPlaybackState = mock(PlaybackState.class);
     private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
     private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+            LocalBluetoothProfileManager.class);
+    private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+            LocalBluetoothLeBroadcast.class);
     private ActivityStarter mStarter = mock(ActivityStarter.class);
     private BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
     private NotificationEntryManager mNotificationEntryManager =
@@ -71,15 +85,26 @@
             NearbyMediaDevicesManager.class);
     private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
 
+    private List<MediaController> mMediaControllers = new ArrayList<>();
     private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
     private MediaOutputController mMediaOutputController;
     private int mHeaderIconRes;
     private IconCompat mIconCompat;
     private CharSequence mHeaderTitle;
     private CharSequence mHeaderSubtitle;
+    private String mStopText;
+    private boolean mIsBroadcasting;
 
     @Before
     public void setUp() {
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+        when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+        when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
+        when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+        mMediaControllers.add(mMediaController);
+        when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotificationEntryManager, mDialogLaunchAnimator,
@@ -173,6 +198,59 @@
         verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
     }
 
+    @Test
+    public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        mIsBroadcasting = true;
+
+        mMediaOutputBaseDialogImpl.onStart();
+
+        verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any());
+    }
+
+    @Test
+    public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        mIsBroadcasting = false;
+
+        mMediaOutputBaseDialogImpl.onStart();
+
+        verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
+    }
+
+    @Test
+    public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        mIsBroadcasting = true;
+
+        mMediaOutputBaseDialogImpl.onStop();
+
+        verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any());
+    }
+
+    @Test
+    public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() {
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        mIsBroadcasting = false;
+
+        mMediaOutputBaseDialogImpl.onStop();
+
+        verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any());
+    }
+
+    @Test
+    public void refresh_checkStopText() {
+        mStopText = "test_string";
+        mMediaOutputBaseDialogImpl.refresh();
+        final Button stop = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(R.id.stop);
+
+        assertThat(stop.getText().toString()).isEqualTo(mStopText);
+    }
+
     class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
 
         MediaOutputBaseDialogImpl(Context context, BroadcastSender broadcastSender,
@@ -216,5 +294,15 @@
         int getStopButtonVisibility() {
             return 0;
         }
+
+        @Override
+        public boolean isBroadcastSupported() {
+            return mIsBroadcasting;
+        }
+
+        @Override
+        public CharSequence getStopButtonText() {
+            return mStopText;
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index db56f87..e6ad6ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -18,13 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.media.MediaRoute2Info;
+import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -32,7 +35,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
@@ -60,7 +65,13 @@
 
     // Mock
     private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+    private MediaController mMediaController = mock(MediaController.class);
+    private PlaybackState mPlaybackState = mock(PlaybackState.class);
     private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+            LocalBluetoothProfileManager.class);
+    private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+            LocalBluetoothLeBroadcast.class);
     private final ActivityStarter mStarter = mock(ActivityStarter.class);
     private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
     private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
@@ -72,12 +83,21 @@
     private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
             NearbyMediaDevicesManager.class);
 
+    private List<MediaController> mMediaControllers = new ArrayList<>();
     private MediaOutputDialog mMediaOutputDialog;
     private MediaOutputController mMediaOutputController;
     private final List<String> mFeatures = new ArrayList<>();
 
     @Before
     public void setUp() {
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
+        when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+        when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
+        when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+        mMediaControllers.add(mMediaController);
+        when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotificationEntryManager, mDialogLaunchAnimator,
@@ -116,6 +136,13 @@
         mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
 
         assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+        mFeatures.clear();
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+                mLocalBluetoothLeBroadcast);
+        when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
+        when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+        assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
     }
 
     @Test
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index c9903ea..e27b7a6 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.graphics.Camera;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
@@ -752,13 +753,65 @@
         public ISessionProcessorImpl getSessionProcessor() {
             return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
         }
+
+        @Override
+        public CameraMetadataNative getAvailableCaptureRequestKeys(String cameraId) {
+            if (RESULT_API_SUPPORTED) {
+                List<CaptureRequest.Key> supportedCaptureKeys =
+                        mAdvancedExtender.getAvailableCaptureRequestKeys();
+
+                if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) {
+                    CameraMetadataNative ret = new CameraMetadataNative();
+                    long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
+                            mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+                    ret.setVendorId(vendorId);
+                    int requestKeyTags [] = new int[supportedCaptureKeys.size()];
+                    int i = 0;
+                    for (CaptureRequest.Key key : supportedCaptureKeys) {
+                        requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+                    }
+                    ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags);
+
+                    return ret;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        public CameraMetadataNative getAvailableCaptureResultKeys(String cameraId) {
+            if (RESULT_API_SUPPORTED) {
+                List<CaptureResult.Key> supportedResultKeys =
+                        mAdvancedExtender.getAvailableCaptureResultKeys();
+
+                if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) {
+                    CameraMetadataNative ret = new CameraMetadataNative();
+                    long vendorId = mMetadataVendorIdMap.containsKey(cameraId) ?
+                            mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+                    ret.setVendorId(vendorId);
+                    int resultKeyTags [] = new int[supportedResultKeys.size()];
+                    int i = 0;
+                    for (CaptureResult.Key key : supportedResultKeys) {
+                        resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+                    }
+                    ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags);
+
+                    return ret;
+                }
+            }
+
+            return null;
+        }
     }
 
     private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
         private final ICaptureCallback mCaptureCallback;
+        private final String mCameraId;
 
-        private CaptureCallbackStub(ICaptureCallback captureCallback) {
+        private CaptureCallbackStub(ICaptureCallback captureCallback, String cameraId) {
             mCaptureCallback = captureCallback;
+            mCameraId = cameraId;
         }
 
         @Override
@@ -820,6 +873,29 @@
                 }
             }
         }
+
+        @Override
+        public void onCaptureCompleted(long timestamp, int requestId,
+                Map<CaptureResult.Key, Object> result) {
+
+            if (result == null) {
+                Log.e(TAG, "Invalid capture result received!");
+            }
+
+            CameraMetadataNative captureResults = new CameraMetadataNative();
+            if (mMetadataVendorIdMap.containsKey(mCameraId)) {
+                captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId));
+            }
+            for (Map.Entry<CaptureResult.Key, Object> entry : result.entrySet()) {
+                captureResults.set(entry.getKey(), entry.getValue());
+            }
+
+            try {
+                mCaptureCallback.onCaptureCompleted(timestamp, requestId, captureResults);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify capture complete due to remote exception!");
+            }
+        }
     }
 
     private class RequestCallbackStub extends IRequestCallback.Stub {
@@ -1124,7 +1200,7 @@
 
         @Override
         public int startRepeating(ICaptureCallback callback) {
-            return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
+            return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback, mCameraId));
         }
 
         @Override
@@ -1133,12 +1209,29 @@
         }
 
         @Override
-        public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
+        public void setParameters(CaptureRequest captureRequest) {
             HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
-            paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
-            paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
+            for (CaptureRequest.Key captureRequestKey : captureRequest.getKeys()) {
+                paramMap.put(captureRequestKey, captureRequest.get(captureRequestKey));
+            }
+
             mSessionProcessor.setParameters(paramMap);
-            return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
+        }
+
+        @Override
+        public int startTrigger(CaptureRequest captureRequest, ICaptureCallback callback) {
+            HashMap<CaptureRequest.Key<?>, Object> triggerMap = new HashMap<>();
+            for (CaptureRequest.Key captureRequestKey : captureRequest.getKeys()) {
+                triggerMap.put(captureRequestKey, captureRequest.get(captureRequestKey));
+            }
+
+            return mSessionProcessor.startTrigger(triggerMap,
+                    new CaptureCallbackStub(callback, mCameraId));
+        }
+
+        @Override
+        public int startCapture(ICaptureCallback callback) {
+            return mSessionProcessor.startCapture(new CaptureCallbackStub(callback, mCameraId));
         }
     }
 
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
index eb16d3b..d1c0482 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
@@ -35,6 +35,7 @@
 
     private static final String TAG = "RemoteCloudSearchService";
 
+    private static final long TIMEOUT_IDLE_BOUND_TIMEOUT_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
 
     private final RemoteCloudSearchServiceCallbacks mCallback;
@@ -57,7 +58,7 @@
 
     @Override
     protected long getTimeoutIdleBindMillis() {
-        return PERMANENT_BOUND_TIMEOUT_MS;
+        return TIMEOUT_IDLE_BOUND_TIMEOUT_MS;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e58d736..7b412a0 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -613,6 +613,10 @@
                     pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
         }
 
+        // SearchSelector
+        grantPermissionsToSystemPackage(pm, getDefaultSearchSelectorPackage(), userId,
+                NOTIFICATION_PERMISSIONS);
+
         // Camera
         grantPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm, MediaStore.ACTION_IMAGE_CAPTURE, userId),
@@ -941,6 +945,10 @@
                 new Intent(Intent.ACTION_MAIN).addCategory(category), userId);
     }
 
+    private String getDefaultSearchSelectorPackage() {
+        return mContext.getString(R.string.config_defaultSearchSelectorPackageName);
+    }
+
     @SafeVarargs
     private final void grantPermissionToEachSystemPackage(PackageManagerWrapper pm,
             ArrayList<String> packages, int userId, Set<String>... permissions) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 186eccc..092f3be 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -2645,12 +2645,9 @@
 
                     // Cache newImplicitPermissions before modifing permissionsState as for the
                     // shared uids the original and new state are the same object
-                    // TODO(205888750): remove the line for LEGACY_REVIEW once propagated through
-                    // droidfood
                     if (!origState.hasPermissionState(permName)
                             && (pkg.getImplicitPermissions().contains(permName)
-                            || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))
-                            || NOTIFICATION_PERMISSIONS.contains(permName)) {
+                            || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
                         if (pkg.getImplicitPermissions().contains(permName)) {
                             // If permName is an implicit permission, try to auto-grant
                             newImplicitPermissions.add(permName);
@@ -2808,12 +2805,14 @@
                             }
 
                             // Remove review flag as it is not necessary anymore
-                            if (!NOTIFICATION_PERMISSIONS.contains(perm)) {
+                            // TODO(b/227186603) re-enable check for notification permission once
+                            // droidfood state has been cleared
+                            //if (!NOTIFICATION_PERMISSIONS.contains(perm)) {
                                 if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                                     flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
                                     wasChanged = true;
                                 }
-                            }
+                            //}
 
                             if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0
                                     && !isPermissionSplitFromNonRuntime(permName,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2b431b6..dc2b6f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1459,8 +1459,9 @@
                     Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
                     TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
                     traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
-                    if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
-                        Slog.e(TAG, "Unable to preload default resources");
+                    String[] abis32 = Build.SUPPORTED_32_BIT_ABIS;
+                    if (abis32.length > 0 && !Process.ZYGOTE_PROCESS.preloadDefault(abis32[0])) {
+                        Slog.e(TAG, "Unable to preload default resources for secondary");
                     }
                     traceLog.traceEnd();
                 } catch (Exception ex) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
index c2cf2ff..14f95e9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java
@@ -107,7 +107,7 @@
     @Test
     public void testWriteHighLevelStateToDisk() {
         long lastReclamationTime = System.currentTimeMillis();
-        long remainingConsumableNarcs = 2000L;
+        long remainingConsumableCakes = 2000L;
         long consumptionLimit = 500_000L;
         when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit);
 
@@ -118,20 +118,20 @@
         ledger.recordTransaction(new Ledger.Transaction(0, 1000L, 1, null, -5000, 3000));
         mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime);
         mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit);
-        mScribeUnderTest.adjustRemainingConsumableNarcsLocked(
-                remainingConsumableNarcs - consumptionLimit);
+        mScribeUnderTest.adjustRemainingConsumableCakesLocked(
+                remainingConsumableCakes - consumptionLimit);
 
         assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
-        assertEquals(remainingConsumableNarcs,
-                mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(remainingConsumableCakes,
+                mScribeUnderTest.getRemainingConsumableCakesLocked());
         assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
 
         mScribeUnderTest.writeImmediatelyForTesting();
         mScribeUnderTest.loadFromDiskLocked();
 
         assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked());
-        assertEquals(remainingConsumableNarcs,
-                mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(remainingConsumableCakes,
+                mScribeUnderTest.getRemainingConsumableCakesLocked());
         assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
     }
 
@@ -234,32 +234,32 @@
     @Test
     public void testChangingConsumable() {
         assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(0, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(0, mScribeUnderTest.getRemainingConsumableCakesLocked());
 
         // Limit increased, so remaining value should be adjusted as well
         mScribeUnderTest.setConsumptionLimitLocked(1000);
         assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(1000, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(1000, mScribeUnderTest.getRemainingConsumableCakesLocked());
 
         // Limit decreased below remaining, so remaining value should be adjusted as well
         mScribeUnderTest.setConsumptionLimitLocked(500);
         assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(500, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(500, mScribeUnderTest.getRemainingConsumableCakesLocked());
 
-        mScribeUnderTest.adjustRemainingConsumableNarcsLocked(-100);
+        mScribeUnderTest.adjustRemainingConsumableCakesLocked(-100);
         assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(400, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(400, mScribeUnderTest.getRemainingConsumableCakesLocked());
 
         // Limit increased, so remaining value should be adjusted by the difference as well
         mScribeUnderTest.setConsumptionLimitLocked(1000);
         assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked());
 
 
         // Limit decreased, but above remaining, so remaining value should left alone
         mScribeUnderTest.setConsumptionLimitLocked(950);
         assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked());
-        assertEquals(900, mScribeUnderTest.getRemainingConsumableNarcsLocked());
+        assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked());
     }
 
     private void assertLedgersEqual(Ledger expected, Ledger actual) {